Merge commit 'e6089b524bbfcc6a3f08fa0d84610c9f26a27bfc' into dev-release
diff --git a/src/main/java/com/android/tools/r8/D8.java b/src/main/java/com/android/tools/r8/D8.java
index 7310ecf..7335dbb 100644
--- a/src/main/java/com/android/tools/r8/D8.java
+++ b/src/main/java/com/android/tools/r8/D8.java
@@ -32,6 +32,7 @@
import com.android.tools.r8.naming.signature.GenericSignatureRewriter;
import com.android.tools.r8.origin.CommandLineOrigin;
import com.android.tools.r8.origin.Origin;
+import com.android.tools.r8.shaking.MainDexInfo;
import com.android.tools.r8.synthesis.SyntheticFinalization;
import com.android.tools.r8.synthesis.SyntheticItems;
import com.android.tools.r8.utils.AndroidApp;
@@ -184,9 +185,9 @@
SyntheticItems.collectSyntheticInputs(appView);
if (!options.mainDexKeepRules.isEmpty()) {
- new GenerateMainDexList(options)
- .traceMainDex(
- executor, appView.appInfo().app(), appView.appInfo().getMainDexClasses()::addAll);
+ MainDexInfo mainDexInfo =
+ new GenerateMainDexList(options).traceMainDex(executor, appView.appInfo().app());
+ appView.setAppInfo(appView.appInfo().rebuildWithMainDexInfo(mainDexInfo));
}
final CfgPrinter printer = options.printCfg ? new CfgPrinter() : null;
@@ -300,7 +301,7 @@
appView.setAppInfo(
new AppInfo(
appView.appInfo().getSyntheticItems().commit(app),
- appView.appInfo().getMainDexClasses()));
+ appView.appInfo().getMainDexInfo()));
namingLens = NamingLens.getIdentityLens();
}
@@ -358,7 +359,7 @@
appView.setAppInfo(
new AppInfo(
appView.appInfo().getSyntheticItems().commit(cfApp),
- appView.appInfo().getMainDexClasses()));
+ appView.appInfo().getMainDexInfo()));
ConvertedCfFiles convertedCfFiles = new ConvertedCfFiles();
NamingLens prefixRewritingNamingLens =
PrefixRewritingNamingLens.createPrefixRewritingNamingLens(appView);
diff --git a/src/main/java/com/android/tools/r8/D8Command.java b/src/main/java/com/android/tools/r8/D8Command.java
index da20df4..c5eac58 100644
--- a/src/main/java/com/android/tools/r8/D8Command.java
+++ b/src/main/java/com/android/tools/r8/D8Command.java
@@ -476,7 +476,6 @@
assert !internal.enableEnumValueOptimization;
assert !internal.outline.enabled;
assert !internal.enableValuePropagation;
- assert !internal.enableLambdaMerging;
assert !internal.enableTreeShakingOfLibraryMethodOverrides;
internal.desugarState = getDesugarState();
diff --git a/src/main/java/com/android/tools/r8/DexSplitterHelper.java b/src/main/java/com/android/tools/r8/DexSplitterHelper.java
index 0230729..7896d57 100644
--- a/src/main/java/com/android/tools/r8/DexSplitterHelper.java
+++ b/src/main/java/com/android/tools/r8/DexSplitterHelper.java
@@ -19,7 +19,7 @@
import com.android.tools.r8.graph.LazyLoadedDexApplication;
import com.android.tools.r8.naming.ClassNameMapper;
import com.android.tools.r8.naming.NamingLens;
-import com.android.tools.r8.shaking.MainDexClasses;
+import com.android.tools.r8.shaking.MainDexInfo;
import com.android.tools.r8.utils.ExceptionUtils;
import com.android.tools.r8.utils.FeatureClassMapping;
import com.android.tools.r8.utils.FeatureClassMapping.FeatureMappingException;
@@ -75,7 +75,7 @@
ApplicationReader applicationReader =
new ApplicationReader(command.getInputApp(), options, timing);
DexApplication app = applicationReader.read(executor);
- MainDexClasses mainDexClasses = applicationReader.readMainDexClasses(app);
+ MainDexInfo mainDexInfo = applicationReader.readMainDexClasses(app);
List<Marker> markers = app.dexItemFactory.extractMarkers();
@@ -94,7 +94,7 @@
// If this is the base, we add the main dex list.
AppInfo appInfo =
feature.equals(featureClassMapping.getBaseName())
- ? AppInfo.createInitialAppInfo(featureApp, mainDexClasses)
+ ? AppInfo.createInitialAppInfo(featureApp, mainDexInfo)
: AppInfo.createInitialAppInfo(featureApp);
AppView<AppInfo> appView = AppView.createForD8(appInfo);
diff --git a/src/main/java/com/android/tools/r8/GenerateMainDexList.java b/src/main/java/com/android/tools/r8/GenerateMainDexList.java
index f91a003..2ebde33 100644
--- a/src/main/java/com/android/tools/r8/GenerateMainDexList.java
+++ b/src/main/java/com/android/tools/r8/GenerateMainDexList.java
@@ -16,9 +16,9 @@
import com.android.tools.r8.graph.SubtypingInfo;
import com.android.tools.r8.shaking.Enqueuer;
import com.android.tools.r8.shaking.EnqueuerFactory;
+import com.android.tools.r8.shaking.MainDexInfo;
import com.android.tools.r8.shaking.MainDexListBuilder;
-import com.android.tools.r8.shaking.MainDexTracingResult;
-import com.android.tools.r8.shaking.RootSetUtils.RootSet;
+import com.android.tools.r8.shaking.RootSetUtils.MainDexRootSet;
import com.android.tools.r8.shaking.WhyAreYouKeepingConsumer;
import com.android.tools.r8.utils.AndroidApp;
import com.android.tools.r8.utils.Box;
@@ -28,20 +28,16 @@
import com.android.tools.r8.utils.Timing;
import java.io.IOException;
import java.util.ArrayList;
+import java.util.Collections;
import java.util.List;
-import java.util.Set;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
-import java.util.function.Consumer;
-import java.util.stream.Collectors;
@Keep
public class GenerateMainDexList {
private final Timing timing = new Timing("maindex");
private final InternalOptions options;
- private List<String> result = null;
-
public GenerateMainDexList(InternalOptions options) {
this.options = options;
}
@@ -49,32 +45,24 @@
private List<String> run(AndroidApp app, ExecutorService executor)
throws IOException {
try {
+ // TODO(b/178231294): Clean up this such that we do not both return the result and call the
+ // consumer.
DexApplication application = new ApplicationReader(app, options, timing).read(executor);
- traceMainDex(
- executor,
- application,
- mainDexTracingResult -> {
- result =
- mainDexTracingResult.getClasses().stream()
- .map(c -> c.toSourceString().replace('.', '/') + ".class")
- .sorted()
- .collect(Collectors.toList());
-
- if (options.mainDexListConsumer != null) {
- options.mainDexListConsumer.accept(String.join("\n", result), options.reporter);
- options.mainDexListConsumer.finished(options.reporter);
- }
- });
+ List<String> result = new ArrayList<>();
+ traceMainDex(executor, application)
+ .forEach(type -> result.add(type.toBinaryName() + ".class"));
+ Collections.sort(result);
+ if (options.mainDexListConsumer != null) {
+ options.mainDexListConsumer.accept(String.join("\n", result), options.reporter);
+ options.mainDexListConsumer.finished(options.reporter);
+ }
return result;
} catch (ExecutionException e) {
throw unwrapExecutionException(e);
}
}
- public void traceMainDex(
- ExecutorService executor,
- DexApplication application,
- Consumer<MainDexTracingResult> resultConsumer)
+ public MainDexInfo traceMainDex(ExecutorService executor, DexApplication application)
throws ExecutionException {
AppView<? extends AppInfoWithClassHierarchy> appView =
AppView.createForR8(application.toDirect());
@@ -84,8 +72,9 @@
SubtypingInfo subtypingInfo = new SubtypingInfo(appView);
- RootSet mainDexRootSet =
- RootSet.builder(appView, subtypingInfo, options.mainDexKeepRules).build(executor);
+ MainDexRootSet mainDexRootSet =
+ MainDexRootSet.builder(appView, subtypingInfo, options.mainDexKeepRules).build(executor);
+ appView.setMainDexRootSet(mainDexRootSet);
GraphConsumer graphConsumer = options.mainDexKeptGraphConsumer;
WhyAreYouKeepingConsumer whyAreYouKeepingConsumer = null;
@@ -95,26 +84,20 @@
}
Enqueuer enqueuer =
- EnqueuerFactory.createForFinalMainDexTracing(
- appView, executor, subtypingInfo, graphConsumer, MainDexTracingResult.NONE);
- Set<DexProgramClass> liveTypes = enqueuer.traceMainDex(mainDexRootSet, executor, timing);
- // LiveTypes is the result.
- MainDexTracingResult mainDexTracingResult = new MainDexListBuilder(liveTypes, appView).run();
- resultConsumer.accept(mainDexTracingResult);
-
+ EnqueuerFactory.createForGenerateMainDexList(
+ appView, executor, subtypingInfo, graphConsumer);
+ MainDexInfo mainDexInfo = enqueuer.traceMainDex(executor, timing);
R8.processWhyAreYouKeepingAndCheckDiscarded(
mainDexRootSet,
() -> {
ArrayList<DexProgramClass> classes = new ArrayList<>();
// TODO(b/131668850): This is not a deterministic order!
- mainDexTracingResult
- .getClasses()
- .forEach(
- type -> {
- DexClass clazz = appView.definitionFor(type);
- assert clazz.isProgramClass();
- classes.add(clazz.asProgramClass());
- });
+ mainDexInfo.forEach(
+ type -> {
+ DexClass clazz = appView.definitionFor(type);
+ assert clazz.isProgramClass();
+ classes.add(clazz.asProgramClass());
+ });
return classes;
},
whyAreYouKeepingConsumer,
@@ -124,6 +107,8 @@
options,
timing,
executor);
+
+ return mainDexInfo;
}
/**
diff --git a/src/main/java/com/android/tools/r8/L8Command.java b/src/main/java/com/android/tools/r8/L8Command.java
index 8bf7cc6..b552bb2 100644
--- a/src/main/java/com/android/tools/r8/L8Command.java
+++ b/src/main/java/com/android/tools/r8/L8Command.java
@@ -174,7 +174,6 @@
assert !internal.enableEnumValueOptimization;
assert !internal.outline.enabled;
assert !internal.enableValuePropagation;
- assert !internal.enableLambdaMerging;
assert !internal.enableTreeShakingOfLibraryMethodOverrides;
assert internal.desugarState == DesugarState.ON;
diff --git a/src/main/java/com/android/tools/r8/PrintUses.java b/src/main/java/com/android/tools/r8/PrintUses.java
index 782094c..80b5ec7 100644
--- a/src/main/java/com/android/tools/r8/PrintUses.java
+++ b/src/main/java/com/android/tools/r8/PrintUses.java
@@ -27,7 +27,7 @@
import com.android.tools.r8.graph.ResolutionResult;
import com.android.tools.r8.graph.UseRegistry;
import com.android.tools.r8.ir.desugar.LambdaDescriptor;
-import com.android.tools.r8.shaking.MainDexClasses;
+import com.android.tools.r8.shaking.MainDexInfo;
import com.android.tools.r8.utils.AndroidApp;
import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.StringUtils;
@@ -364,7 +364,7 @@
AppInfoWithClassHierarchy.createInitialAppInfoWithClassHierarchy(
application,
ClassToFeatureSplitMap.createEmptyClassToFeatureSplitMap(),
- MainDexClasses.createEmptyMainDexClasses());
+ MainDexInfo.none());
}
private void analyze() {
diff --git a/src/main/java/com/android/tools/r8/R8.java b/src/main/java/com/android/tools/r8/R8.java
index 9c93e0e..283e461 100644
--- a/src/main/java/com/android/tools/r8/R8.java
+++ b/src/main/java/com/android/tools/r8/R8.java
@@ -88,12 +88,12 @@
import com.android.tools.r8.shaking.Enqueuer;
import com.android.tools.r8.shaking.Enqueuer.Mode;
import com.android.tools.r8.shaking.EnqueuerFactory;
-import com.android.tools.r8.shaking.MainDexClasses;
+import com.android.tools.r8.shaking.MainDexInfo;
import com.android.tools.r8.shaking.MainDexListBuilder;
-import com.android.tools.r8.shaking.MainDexTracingResult;
import com.android.tools.r8.shaking.MissingClasses;
import com.android.tools.r8.shaking.ProguardConfigurationRule;
import com.android.tools.r8.shaking.ProguardConfigurationUtils;
+import com.android.tools.r8.shaking.RootSetUtils.MainDexRootSet;
import com.android.tools.r8.shaking.RootSetUtils.RootSet;
import com.android.tools.r8.shaking.RootSetUtils.RootSetBuilder;
import com.android.tools.r8.shaking.RuntimeTypeCheckInfo;
@@ -284,12 +284,12 @@
{
ApplicationReader applicationReader = new ApplicationReader(inputApp, options, timing);
DirectMappedDexApplication application = applicationReader.read(executorService).toDirect();
- MainDexClasses mainDexClasses = applicationReader.readMainDexClasses(application);
+ MainDexInfo mainDexInfo = applicationReader.readMainDexClasses(application);
// Now that the dex-application is fully loaded, close any internal archive providers.
inputApp.closeInternalArchiveProviders();
- appView = AppView.createForR8(application, mainDexClasses);
+ appView = AppView.createForR8(application, mainDexInfo);
appView.setAppServices(AppServices.builder(appView).build());
}
@@ -326,7 +326,8 @@
.appInfo()
.rebuildWithClassHierarchy(
MissingClasses.builderForInitialMissingClasses()
- .addNewMissingClasses(new SubtypingInfo(appView).getMissingClasses())
+ .legacyAddNewMissingClasses(
+ new SubtypingInfo(appView).getMissingClasses())
.reportMissingClasses(appView)));
}
@@ -421,23 +422,7 @@
// Build conservative main dex content after first round of tree shaking. This is used
// by certain optimizations to avoid introducing additional class references into main dex
// classes, as that can cause the final number of main dex methods to grow.
- RootSet mainDexRootSet = null;
- MainDexTracingResult mainDexTracingResult = MainDexTracingResult.NONE;
- if (!options.mainDexKeepRules.isEmpty()) {
- assert appView.graphLens().isIdentityLens();
- // Find classes which may have code executed before secondary dex files installation.
- SubtypingInfo subtypingInfo = new SubtypingInfo(appView);
- mainDexRootSet =
- RootSet.builder(appView, subtypingInfo, options.mainDexKeepRules)
- .build(executorService);
- // Live types is the tracing result.
- Set<DexProgramClass> mainDexBaseClasses =
- EnqueuerFactory.createForInitialMainDexTracing(appView, executorService, subtypingInfo)
- .traceMainDex(mainDexRootSet, executorService, timing);
- // Calculate the automatic main dex list according to legacy multidex constraints.
- mainDexTracingResult = new MainDexListBuilder(mainDexBaseClasses, appView).run();
- appView.appInfo().unsetObsolete();
- }
+ performInitialMainDexTracing(appView, executorService);
// The class type lattice elements include information about the interfaces that a class
// implements. This information can change as a result of vertical class merging, so we need
@@ -479,11 +464,7 @@
timing.begin("VerticalClassMerger");
VerticalClassMerger verticalClassMerger =
new VerticalClassMerger(
- getDirectApp(appViewWithLiveness),
- appViewWithLiveness,
- executorService,
- timing,
- mainDexTracingResult);
+ getDirectApp(appViewWithLiveness), appViewWithLiveness, executorService, timing);
VerticalClassMergerGraphLens lens = verticalClassMerger.run();
if (lens != null) {
appView.rewriteWithLens(lens);
@@ -530,7 +511,7 @@
DirectMappedDexApplication.Builder appBuilder =
appView.appInfo().app().asDirect().builder();
HorizontalClassMergerResult horizontalClassMergerResult =
- merger.run(appBuilder, mainDexTracingResult, runtimeTypeCheckInfo);
+ merger.run(appBuilder, runtimeTypeCheckInfo);
if (horizontalClassMergerResult != null) {
// Must rewrite AppInfoWithLiveness before pruning the merged classes, to ensure that
// allocations sites, fields accesses, etc. are correctly transferred to the target
@@ -547,8 +528,6 @@
.addRemovedClasses(appView.horizontallyMergedClasses().getSources())
.addNoLongerSyntheticItems(appView.horizontallyMergedClasses().getTargets())
.build());
-
- mainDexTracingResult = horizontalClassMergerResult.getMainDexTracingResult();
}
timing.end();
} else {
@@ -556,6 +535,9 @@
}
}
+ // Clear traced methods roots to not hold on to the main dex live method set.
+ appView.appInfo().getMainDexInfo().clearTracedMethodRoots();
+
// None of the optimizations above should lead to the creation of type lattice elements.
assert appView.dexItemFactory().verifyNoCachedTypeElements();
@@ -571,7 +553,7 @@
timing.begin("Create IR");
CfgPrinter printer = options.printCfg ? new CfgPrinter() : null;
try {
- IRConverter converter = new IRConverter(appView, timing, printer, mainDexTracingResult);
+ IRConverter converter = new IRConverter(appView, timing, printer);
DexApplication application =
converter.optimize(appViewWithLiveness, executorService).asDirect();
appView.setAppInfo(appView.appInfo().rebuildWithClassHierarchy(previous -> application));
@@ -601,55 +583,6 @@
}
}
- if (!options.mainDexKeepRules.isEmpty()) {
- // No need to build a new main dex root set
- assert mainDexRootSet != null;
- GraphConsumer mainDexKeptGraphConsumer = options.mainDexKeptGraphConsumer;
- WhyAreYouKeepingConsumer whyAreYouKeepingConsumer = null;
- if (!mainDexRootSet.reasonAsked.isEmpty()) {
- whyAreYouKeepingConsumer = new WhyAreYouKeepingConsumer(mainDexKeptGraphConsumer);
- mainDexKeptGraphConsumer = whyAreYouKeepingConsumer;
- }
-
- Enqueuer enqueuer =
- EnqueuerFactory.createForFinalMainDexTracing(
- appView,
- executorService,
- new SubtypingInfo(appView),
- mainDexKeptGraphConsumer,
- mainDexTracingResult);
- // Find classes which may have code executed before secondary dex files installation.
- // Live types is the tracing result.
- Set<DexProgramClass> mainDexBaseClasses =
- enqueuer.traceMainDex(mainDexRootSet, executorService, timing);
- // Calculate the automatic main dex list according to legacy multidex constraints.
- mainDexTracingResult = new MainDexListBuilder(mainDexBaseClasses, appView).run();
- final MainDexTracingResult finalMainDexClasses = mainDexTracingResult;
-
- processWhyAreYouKeepingAndCheckDiscarded(
- mainDexRootSet,
- () -> {
- ArrayList<DexProgramClass> classes = new ArrayList<>();
- // TODO(b/131668850): This is not a deterministic order!
- finalMainDexClasses
- .getClasses()
- .forEach(
- type -> {
- DexClass clazz = appView.definitionFor(type);
- assert clazz.isProgramClass();
- classes.add(clazz.asProgramClass());
- });
- return classes;
- },
- whyAreYouKeepingConsumer,
- appView,
- enqueuer,
- true,
- options,
- timing,
- executorService);
- }
-
if (options.shouldRerunEnqueuer()) {
timing.begin("Post optimization code stripping");
try {
@@ -731,12 +664,6 @@
.setClassesToRetainInnerClassAttributeFor(classesToRetainInnerClassAttributeFor)
.build(appView.withLiveness(), removedClasses)
.run();
- if (!mainDexTracingResult.isEmpty()) {
- // Remove types that no longer exists from the computed main dex list.
- mainDexTracingResult =
- mainDexTracingResult.prunedCopy(appView.appInfo().withLiveness());
- }
-
// Synthesize fields for triggering class initializers.
new ClassInitFieldSynthesizer(appViewWithLiveness).run(executorService);
}
@@ -749,7 +676,7 @@
appView.protoShrinker().enumLiteProtoShrinker.verifyDeadEnumLiteMapsAreDead();
}
- IRConverter converter = new IRConverter(appView, timing, null, mainDexTracingResult);
+ IRConverter converter = new IRConverter(appView, timing, null);
// 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
@@ -767,6 +694,8 @@
}
}
+ performFinalMainDexTracing(appView, executorService);
+
// Remove unneeded visibility bridges that have been inserted for member rebinding.
// This can only be done if we have AppInfoWithLiveness.
if (appView.appInfo().hasLiveness()) {
@@ -803,11 +732,6 @@
assert Repackaging.verifyIdentityRepackaging(appView.withLiveness());
}
- // Add automatic main dex classes to an eventual manual list of classes.
- if (!options.mainDexKeepRules.isEmpty()) {
- appView.appInfo().getMainDexClasses().addAll(mainDexTracingResult);
- }
-
if (appView.appInfo().hasLiveness()) {
SyntheticFinalization.finalizeWithLiveness(appView.withLiveness());
} else {
@@ -913,6 +837,71 @@
}
}
+ private void performInitialMainDexTracing(
+ AppView<AppInfoWithClassHierarchy> appView, ExecutorService executorService)
+ throws ExecutionException {
+ if (options.mainDexKeepRules.isEmpty()) {
+ return;
+ }
+ assert appView.graphLens().isIdentityLens();
+ // Find classes which may have code executed before secondary dex files installation.
+ SubtypingInfo subtypingInfo = new SubtypingInfo(appView);
+ MainDexRootSet mainDexRootSet =
+ MainDexRootSet.builder(appView, subtypingInfo, options.mainDexKeepRules)
+ .build(executorService);
+ appView.setMainDexRootSet(mainDexRootSet);
+ appView.appInfo().unsetObsolete();
+ // Live types is the tracing result.
+ MainDexInfo mainDexInfo =
+ EnqueuerFactory.createForInitialMainDexTracing(appView, executorService, subtypingInfo)
+ .traceMainDex(executorService, timing);
+ appView.setAppInfo(appView.appInfo().rebuildWithMainDexInfo(mainDexInfo));
+ }
+
+ private void performFinalMainDexTracing(
+ AppView<AppInfoWithClassHierarchy> appView, ExecutorService executorService)
+ throws ExecutionException {
+ if (options.mainDexKeepRules.isEmpty()) {
+ return;
+ }
+ // No need to build a new main dex root set
+ assert appView.getMainDexRootSet() != null;
+ GraphConsumer mainDexKeptGraphConsumer = options.mainDexKeptGraphConsumer;
+ WhyAreYouKeepingConsumer whyAreYouKeepingConsumer = null;
+ if (!appView.getMainDexRootSet().reasonAsked.isEmpty()) {
+ whyAreYouKeepingConsumer = new WhyAreYouKeepingConsumer(mainDexKeptGraphConsumer);
+ mainDexKeptGraphConsumer = whyAreYouKeepingConsumer;
+ }
+
+ Enqueuer enqueuer =
+ EnqueuerFactory.createForFinalMainDexTracing(
+ appView, executorService, new SubtypingInfo(appView), mainDexKeptGraphConsumer);
+ // Find classes which may have code executed before secondary dex files installation.
+ MainDexInfo mainDexInfo = enqueuer.traceMainDex(executorService, timing);
+ appView.setAppInfo(appView.appInfo().rebuildWithMainDexInfo(mainDexInfo));
+
+ processWhyAreYouKeepingAndCheckDiscarded(
+ appView.getMainDexRootSet(),
+ () -> {
+ ArrayList<DexProgramClass> classes = new ArrayList<>();
+ // TODO(b/131668850): This is not a deterministic order!
+ mainDexInfo.forEach(
+ type -> {
+ DexClass clazz = appView.definitionFor(type);
+ assert clazz.isProgramClass();
+ classes.add(clazz.asProgramClass());
+ });
+ return classes;
+ },
+ whyAreYouKeepingConsumer,
+ appView,
+ enqueuer,
+ true,
+ options,
+ timing,
+ executorService);
+ }
+
private static boolean verifyMovedMethodsHaveOriginalMethodPosition(
AppView<?> appView, DirectMappedDexApplication application) {
application
@@ -1042,12 +1031,8 @@
if (forMainDex) {
enqueuer =
EnqueuerFactory.createForFinalMainDexTracing(
- appView,
- executorService,
- subtypingInfo,
- whyAreYouKeepingConsumer,
- MainDexTracingResult.NONE);
- enqueuer.traceMainDex(rootSet, executorService, timing);
+ appView, executorService, subtypingInfo, whyAreYouKeepingConsumer);
+ enqueuer.traceMainDex(executorService, timing);
} else {
enqueuer =
EnqueuerFactory.createForWhyAreYouKeeping(
diff --git a/src/main/java/com/android/tools/r8/R8Command.java b/src/main/java/com/android/tools/r8/R8Command.java
index 0fa55a7..8d07b18 100644
--- a/src/main/java/com/android/tools/r8/R8Command.java
+++ b/src/main/java/com/android/tools/r8/R8Command.java
@@ -834,10 +834,6 @@
internal.desugarState = getDesugarState();
assert internal.isShrinking() == getEnableTreeShaking();
assert internal.isMinifying() == getEnableMinification();
- // In current implementation we only enable lambda merger if the tree
- // shaking is enabled. This is caused by the fact that we rely on tree
- // shaking for removing the lambda classes which should be revised later.
- internal.enableLambdaMerging = getEnableTreeShaking();
assert !internal.ignoreMissingClasses;
internal.ignoreMissingClasses =
proguardConfiguration.isIgnoreWarnings()
@@ -871,14 +867,12 @@
internal.enableClassStaticizer = false;
internal.outline.enabled = false;
internal.enableEnumUnboxing = false;
- internal.enableLambdaMerging = false;
}
if (!internal.isShrinking()) {
// If R8 is not shrinking, there is no point in running various optimizations since the
// optimized classes will still remain in the program (the application size could increase).
internal.enableEnumUnboxing = false;
internal.horizontalClassMergerOptions().disable();
- internal.enableLambdaMerging = false;
internal.enableVerticalClassMerging = false;
}
diff --git a/src/main/java/com/android/tools/r8/dex/ApplicationReader.java b/src/main/java/com/android/tools/r8/dex/ApplicationReader.java
index 4b035d4..21de6ac 100644
--- a/src/main/java/com/android/tools/r8/dex/ApplicationReader.java
+++ b/src/main/java/com/android/tools/r8/dex/ApplicationReader.java
@@ -29,7 +29,7 @@
import com.android.tools.r8.graph.JarClassFileReader;
import com.android.tools.r8.graph.LazyLoadedDexApplication;
import com.android.tools.r8.naming.ClassNameMapper;
-import com.android.tools.r8.shaking.MainDexClasses;
+import com.android.tools.r8.shaking.MainDexInfo;
import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.AndroidApp;
import com.android.tools.r8.utils.ClassProvider;
@@ -198,8 +198,8 @@
}
}
- public MainDexClasses readMainDexClasses(DexApplication app) {
- MainDexClasses.Builder builder = MainDexClasses.builder();
+ public MainDexInfo readMainDexClasses(DexApplication app) {
+ MainDexInfo.Builder builder = MainDexInfo.none().builder();
if (inputApp.hasMainDexList()) {
for (StringResource resource : inputApp.getMainDexListResources()) {
addToMainDexClasses(app, builder, MainDexListParser.parseList(resource, itemFactory));
@@ -211,15 +211,15 @@
.map(clazz -> itemFactory.createType(DescriptorUtils.javaTypeToDescriptor(clazz)))
.collect(Collectors.toList()));
}
- return builder.build();
+ return builder.buildList();
}
private void addToMainDexClasses(
- DexApplication app, MainDexClasses.Builder builder, Iterable<DexType> types) {
+ DexApplication app, MainDexInfo.Builder builder, Iterable<DexType> types) {
for (DexType type : types) {
DexProgramClass clazz = app.programDefinitionFor(type);
if (clazz != null) {
- builder.add(clazz);
+ builder.addList(clazz);
} else if (!options.ignoreMainDexMissingClasses) {
options.reporter.warning(
new StringDiagnostic(
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 ab72b04..da3b04c 100644
--- a/src/main/java/com/android/tools/r8/dex/ApplicationWriter.java
+++ b/src/main/java/com/android/tools/r8/dex/ApplicationWriter.java
@@ -45,7 +45,7 @@
import com.android.tools.r8.naming.ProguardMapSupplier;
import com.android.tools.r8.naming.ProguardMapSupplier.ProguardMapId;
import com.android.tools.r8.origin.Origin;
-import com.android.tools.r8.shaking.MainDexClasses;
+import com.android.tools.r8.shaking.MainDexInfo;
import com.android.tools.r8.utils.ArrayUtils;
import com.android.tools.r8.utils.DescriptorUtils;
import com.android.tools.r8.utils.ExceptionUtils;
@@ -199,7 +199,7 @@
options.getDexFilePerClassFileConsumer().combineSyntheticClassesWithPrimaryClass());
} else if (!options.canUseMultidex()
&& options.mainDexKeepRules.isEmpty()
- && appView.appInfo().getMainDexClasses().isEmpty()
+ && appView.appInfo().getMainDexInfo().isEmpty()
&& options.enableMainDexListCheck) {
distributor = new VirtualFile.MonoDexDistributor(this, options);
} else {
@@ -688,10 +688,11 @@
}
private static String writeMainDexList(AppView<?> appView, NamingLens namingLens) {
- MainDexClasses mainDexClasses = appView.appInfo().getMainDexClasses();
+ // TODO(b/178231294): Clean up by streaming directly to the consumer.
+ MainDexInfo mainDexInfo = appView.appInfo().getMainDexInfo();
StringBuilder builder = new StringBuilder();
- List<DexType> list = new ArrayList<>(mainDexClasses.size());
- mainDexClasses.forEach(list::add);
+ List<DexType> list = new ArrayList<>(mainDexInfo.size());
+ mainDexInfo.forEach(list::add);
list.sort(DexType::compareTo);
list.forEach(
type -> builder.append(mapMainDexListName(type, namingLens)).append('\n'));
diff --git a/src/main/java/com/android/tools/r8/dex/Constants.java b/src/main/java/com/android/tools/r8/dex/Constants.java
index dfa8667..8ea7319 100644
--- a/src/main/java/com/android/tools/r8/dex/Constants.java
+++ b/src/main/java/com/android/tools/r8/dex/Constants.java
@@ -135,6 +135,7 @@
public static final int ACC_ANNOTATION = 0x2000;
public static final int ACC_ENUM = 0x4000;
public static final int ACC_CONSTRUCTOR = 0x10000;
+ public static final int ACC_RECORD = 0x10000;
public static final int ACC_DECLARED_SYNCHRONIZED = 0x20000;
public static final String JAVA_LANG_OBJECT_NAME = "java/lang/Object";
diff --git a/src/main/java/com/android/tools/r8/dex/VirtualFile.java b/src/main/java/com/android/tools/r8/dex/VirtualFile.java
index 414031e..8f56827 100644
--- a/src/main/java/com/android/tools/r8/dex/VirtualFile.java
+++ b/src/main/java/com/android/tools/r8/dex/VirtualFile.java
@@ -27,7 +27,7 @@
import com.android.tools.r8.logging.Log;
import com.android.tools.r8.naming.ClassNameMapper;
import com.android.tools.r8.naming.NamingLens;
-import com.android.tools.r8.shaking.MainDexClasses;
+import com.android.tools.r8.shaking.MainDexInfo;
import com.android.tools.r8.synthesis.SyntheticNaming;
import com.android.tools.r8.utils.DescriptorUtils;
import com.android.tools.r8.utils.FileUtils;
@@ -394,13 +394,14 @@
}
protected void fillForMainDexList(Set<DexProgramClass> classes) {
- MainDexClasses mainDexClasses = appView.appInfo().getMainDexClasses();
- if (mainDexClasses.isEmpty()) {
+ MainDexInfo mainDexInfo = appView.appInfo().getMainDexInfo();
+ if (mainDexInfo.isEmpty()) {
return;
}
VirtualFile mainDexFile = virtualFiles.get(0);
- mainDexClasses.forEach(
+ mainDexInfo.forEach(
type -> {
+ // TODO(b/178577273): We should ensure only live types in main dex.
DexProgramClass clazz =
asProgramClassOrNull(appView.appInfo().definitionForWithoutExistenceAssert(type));
if (clazz != null) {
diff --git a/src/main/java/com/android/tools/r8/graph/AppInfo.java b/src/main/java/com/android/tools/r8/graph/AppInfo.java
index 302dff4..de3d976 100644
--- a/src/main/java/com/android/tools/r8/graph/AppInfo.java
+++ b/src/main/java/com/android/tools/r8/graph/AppInfo.java
@@ -7,7 +7,7 @@
import com.android.tools.r8.ir.desugar.InterfaceMethodRewriter;
import com.android.tools.r8.origin.Origin;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
-import com.android.tools.r8.shaking.MainDexClasses;
+import com.android.tools.r8.shaking.MainDexInfo;
import com.android.tools.r8.synthesis.CommittedItems;
import com.android.tools.r8.synthesis.SyntheticItems;
import com.android.tools.r8.utils.BooleanBox;
@@ -20,7 +20,7 @@
private final DexApplication app;
private final DexItemFactory dexItemFactory;
- private final MainDexClasses mainDexClasses;
+ private final MainDexInfo mainDexInfo;
private final SyntheticItems syntheticItems;
// Set when a new AppInfo replaces a previous one. All public methods should verify that the
@@ -28,37 +28,36 @@
private final BooleanBox obsolete;
public static AppInfo createInitialAppInfo(DexApplication application) {
- return createInitialAppInfo(application, MainDexClasses.createEmptyMainDexClasses());
+ return createInitialAppInfo(application, MainDexInfo.none());
}
- public static AppInfo createInitialAppInfo(
- DexApplication application, MainDexClasses mainDexClasses) {
- return new AppInfo(SyntheticItems.createInitialSyntheticItems(application), mainDexClasses);
+ public static AppInfo createInitialAppInfo(DexApplication application, MainDexInfo mainDexInfo) {
+ return new AppInfo(SyntheticItems.createInitialSyntheticItems(application), mainDexInfo);
}
- public AppInfo(CommittedItems committedItems, MainDexClasses mainDexClasses) {
+ public AppInfo(CommittedItems committedItems, MainDexInfo mainDexInfo) {
this(
committedItems.getApplication(),
committedItems.toSyntheticItems(),
- mainDexClasses,
+ mainDexInfo,
new BooleanBox());
}
// For desugaring.
// This is a view onto the app info and is the only place the pending synthetics are shared.
AppInfo(AppInfoWithClassHierarchy.CreateDesugaringViewOnAppInfo witness, AppInfo appInfo) {
- this(appInfo.app, appInfo.syntheticItems, appInfo.mainDexClasses, appInfo.obsolete);
+ this(appInfo.app, appInfo.syntheticItems, appInfo.mainDexInfo, appInfo.obsolete);
assert witness != null;
}
private AppInfo(
DexApplication application,
SyntheticItems syntheticItems,
- MainDexClasses mainDexClasses,
+ MainDexInfo mainDexInfo,
BooleanBox obsolete) {
this.app = application;
this.dexItemFactory = application.dexItemFactory;
- this.mainDexClasses = mainDexClasses;
+ this.mainDexInfo = mainDexInfo;
this.syntheticItems = syntheticItems;
this.obsolete = obsolete;
}
@@ -72,7 +71,12 @@
}
return new AppInfo(
getSyntheticItems().commitPrunedItems(prunedItems),
- getMainDexClasses().withoutPrunedItems(prunedItems));
+ getMainDexInfo().withoutPrunedItems(prunedItems));
+ }
+
+ public AppInfo rebuildWithMainDexInfo(MainDexInfo mainDexInfo) {
+ assert checkIfObsolete();
+ return new AppInfo(app, syntheticItems, mainDexInfo, new BooleanBox());
}
protected InternalOptions options() {
@@ -107,19 +111,29 @@
return dexItemFactory;
}
- public MainDexClasses getMainDexClasses() {
- return mainDexClasses;
+ public MainDexInfo getMainDexInfo() {
+ assert checkIfObsolete();
+ return mainDexInfo;
}
public SyntheticItems getSyntheticItems() {
+ assert checkIfObsolete();
return syntheticItems;
}
- public void addSynthesizedClass(DexProgramClass clazz, boolean addToMainDexClasses) {
+ public void addSynthesizedClass(DexProgramClass clazz, boolean addToMainDex) {
assert checkIfObsolete();
syntheticItems.addLegacySyntheticClass(clazz);
- if (addToMainDexClasses && !mainDexClasses.isEmpty()) {
- mainDexClasses.add(clazz);
+ if (addToMainDex) {
+ mainDexInfo.addSyntheticClass(clazz);
+ }
+ }
+
+ public void addSynthesizedClass(DexProgramClass clazz, ProgramDefinition context) {
+ assert checkIfObsolete();
+ syntheticItems.addLegacySyntheticClass(clazz);
+ if (context != null) {
+ mainDexInfo.addLegacySyntheticClass(clazz, context);
}
}
diff --git a/src/main/java/com/android/tools/r8/graph/AppInfoWithClassHierarchy.java b/src/main/java/com/android/tools/r8/graph/AppInfoWithClassHierarchy.java
index 8ff4d1e..cd90e16 100644
--- a/src/main/java/com/android/tools/r8/graph/AppInfoWithClassHierarchy.java
+++ b/src/main/java/com/android/tools/r8/graph/AppInfoWithClassHierarchy.java
@@ -18,7 +18,7 @@
import com.android.tools.r8.ir.analysis.type.InterfaceCollection;
import com.android.tools.r8.ir.analysis.type.InterfaceCollection.Builder;
import com.android.tools.r8.ir.desugar.LambdaDescriptor;
-import com.android.tools.r8.shaking.MainDexClasses;
+import com.android.tools.r8.shaking.MainDexInfo;
import com.android.tools.r8.shaking.MissingClasses;
import com.android.tools.r8.synthesis.CommittedItems;
import com.android.tools.r8.synthesis.SyntheticItems;
@@ -56,11 +56,11 @@
public static AppInfoWithClassHierarchy createInitialAppInfoWithClassHierarchy(
DexApplication application,
ClassToFeatureSplitMap classToFeatureSplitMap,
- MainDexClasses mainDexClasses) {
+ MainDexInfo mainDexInfo) {
return new AppInfoWithClassHierarchy(
SyntheticItems.createInitialSyntheticItems(application),
classToFeatureSplitMap,
- mainDexClasses,
+ mainDexInfo,
MissingClasses.empty());
}
@@ -74,9 +74,9 @@
protected AppInfoWithClassHierarchy(
CommittedItems committedItems,
ClassToFeatureSplitMap classToFeatureSplitMap,
- MainDexClasses mainDexClasses,
+ MainDexInfo mainDexInfo,
MissingClasses missingClasses) {
- super(committedItems, mainDexClasses);
+ super(committedItems, mainDexInfo);
this.classToFeatureSplitMap = classToFeatureSplitMap;
this.missingClasses = missingClasses;
}
@@ -97,27 +97,36 @@
public final AppInfoWithClassHierarchy rebuildWithClassHierarchy(CommittedItems commit) {
return new AppInfoWithClassHierarchy(
- commit, getClassToFeatureSplitMap(), getMainDexClasses(), getMissingClasses());
+ commit, getClassToFeatureSplitMap(), getMainDexInfo(), getMissingClasses());
}
public final AppInfoWithClassHierarchy rebuildWithClassHierarchy(MissingClasses missingClasses) {
return new AppInfoWithClassHierarchy(
getSyntheticItems().commit(app()),
getClassToFeatureSplitMap(),
- getMainDexClasses(),
+ getMainDexInfo(),
missingClasses);
}
public AppInfoWithClassHierarchy rebuildWithClassHierarchy(
Function<DexApplication, DexApplication> fn) {
+ assert checkIfObsolete();
return new AppInfoWithClassHierarchy(
getSyntheticItems().commit(fn.apply(app())),
getClassToFeatureSplitMap(),
- getMainDexClasses(),
+ getMainDexInfo(),
getMissingClasses());
}
@Override
+ public AppInfoWithClassHierarchy rebuildWithMainDexInfo(MainDexInfo mainDexInfo) {
+ assert getClass() == AppInfoWithClassHierarchy.class;
+ assert checkIfObsolete();
+ return new AppInfoWithClassHierarchy(
+ getSyntheticItems().commit(app()), classToFeatureSplitMap, mainDexInfo, missingClasses);
+ }
+
+ @Override
public AppInfoWithClassHierarchy prunedCopyFrom(PrunedItems prunedItems) {
assert getClass() == AppInfoWithClassHierarchy.class;
assert checkIfObsolete();
@@ -128,7 +137,7 @@
return new AppInfoWithClassHierarchy(
getSyntheticItems().commitPrunedItems(prunedItems),
getClassToFeatureSplitMap().withoutPrunedItems(prunedItems),
- getMainDexClasses().withoutPrunedItems(prunedItems),
+ getMainDexInfo().withoutPrunedItems(prunedItems),
getMissingClasses());
}
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 7d574dd..b56d682 100644
--- a/src/main/java/com/android/tools/r8/graph/AppView.java
+++ b/src/main/java/com/android/tools/r8/graph/AppView.java
@@ -9,7 +9,6 @@
import com.android.tools.r8.graph.DexValue.DexValueString;
import com.android.tools.r8.graph.GraphLens.NonIdentityGraphLens;
import com.android.tools.r8.graph.analysis.InitializedClassesInInstanceMethodsAnalysis.InitializedClassesInInstanceMethods;
-import com.android.tools.r8.graph.classmerging.HorizontallyMergedLambdaClasses;
import com.android.tools.r8.graph.classmerging.MergedClassesCollection;
import com.android.tools.r8.graph.classmerging.VerticallyMergedClasses;
import com.android.tools.r8.horizontalclassmerging.HorizontallyMergedClasses;
@@ -32,8 +31,9 @@
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.android.tools.r8.shaking.KeepInfoCollection;
import com.android.tools.r8.shaking.LibraryModeledPredicate;
-import com.android.tools.r8.shaking.MainDexClasses;
+import com.android.tools.r8.shaking.MainDexInfo;
import com.android.tools.r8.shaking.ProguardCompatibilityActions;
+import com.android.tools.r8.shaking.RootSetUtils.MainDexRootSet;
import com.android.tools.r8.shaking.RootSetUtils.RootSet;
import com.android.tools.r8.synthesis.SyntheticItems;
import com.android.tools.r8.utils.InternalOptions;
@@ -64,6 +64,7 @@
private InitClassLens initClassLens;
private ProguardCompatibilityActions proguardCompatibilityActions;
private RootSet rootSet;
+ private MainDexRootSet mainDexRootSet = null;
// This should perferably always be obtained via AppInfoWithLiveness.
// Currently however the liveness may be downgraded thus loosing the computed keep info.
private KeepInfoCollection keepInfo = null;
@@ -91,13 +92,11 @@
private boolean allCodeProcessed = false;
private Predicate<DexType> classesEscapingIntoLibrary = Predicates.alwaysTrue();
private InitializedClassesInInstanceMethods initializedClassesInInstanceMethods;
- private HorizontallyMergedLambdaClasses horizontallyMergedLambdaClasses;
private HorizontallyMergedClasses horizontallyMergedClasses;
private VerticallyMergedClasses verticallyMergedClasses;
private EnumDataMap unboxedEnums = EnumDataMap.empty();
// TODO(b/169115389): Remove
private Set<DexMethod> cfByteCodePassThrough = ImmutableSet.of();
-
private Map<DexType, DexValueString> sourceDebugExtensions = new IdentityHashMap<>();
// When input has been (partially) desugared these are the classes which has been library
@@ -157,16 +156,16 @@
}
public static AppView<AppInfoWithClassHierarchy> createForR8(DexApplication application) {
- return createForR8(application, MainDexClasses.createEmptyMainDexClasses());
+ return createForR8(application, MainDexInfo.none());
}
public static AppView<AppInfoWithClassHierarchy> createForR8(
- DexApplication application, MainDexClasses mainDexClasses) {
+ DexApplication application, MainDexInfo mainDexInfo) {
ClassToFeatureSplitMap classToFeatureSplitMap =
ClassToFeatureSplitMap.createInitialClassToFeatureSplitMap(application.options);
AppInfoWithClassHierarchy appInfo =
AppInfoWithClassHierarchy.createInitialAppInfoWithClassHierarchy(
- application, classToFeatureSplitMap, mainDexClasses);
+ application, classToFeatureSplitMap, mainDexInfo);
return new AppView<>(
appInfo, WholeProgramOptimizations.ON, defaultPrefixRewritingMapper(appInfo));
}
@@ -460,6 +459,15 @@
this.rootSet = rootSet;
}
+ public void setMainDexRootSet(MainDexRootSet mainDexRootSet) {
+ assert mainDexRootSet != null : "Root set should never be recomputed";
+ this.mainDexRootSet = mainDexRootSet;
+ }
+
+ public MainDexRootSet getMainDexRootSet() {
+ return mainDexRootSet;
+ }
+
public KeepInfoCollection getKeepInfo() {
return keepInfo;
}
@@ -483,9 +491,6 @@
if (horizontallyMergedClasses != null) {
collection.add(horizontallyMergedClasses);
}
- if (horizontallyMergedLambdaClasses != null) {
- collection.add(horizontallyMergedLambdaClasses);
- }
if (verticallyMergedClasses != null) {
collection.add(verticallyMergedClasses);
}
@@ -493,23 +498,6 @@
}
/**
- * Get the result of horizontal lambda class merging. Returns null if horizontal lambda class
- * merging has not been run.
- */
- public HorizontallyMergedLambdaClasses horizontallyMergedLambdaClasses() {
- return horizontallyMergedLambdaClasses;
- }
-
- public void setHorizontallyMergedLambdaClasses(
- HorizontallyMergedLambdaClasses horizontallyMergedLambdaClasses) {
- assert this.horizontallyMergedLambdaClasses == null;
- this.horizontallyMergedLambdaClasses = horizontallyMergedLambdaClasses;
- testing()
- .horizontallyMergedLambdaClassesConsumer
- .accept(dexItemFactory(), horizontallyMergedLambdaClasses);
- }
-
- /**
* Get the result of horizontal class merging. Returns null if horizontal class merging has not
* been run.
*/
@@ -619,6 +607,9 @@
setProguardCompatibilityActions(
getProguardCompatibilityActions().withoutPrunedItems(prunedItems));
}
+ if (mainDexRootSet != null) {
+ setMainDexRootSet(mainDexRootSet.withoutPrunedItems(prunedItems));
+ }
}
@SuppressWarnings("unchecked")
@@ -688,6 +679,9 @@
appView.setProguardCompatibilityActions(
appView.getProguardCompatibilityActions().rewrittenWithLens(lens));
}
+ if (appView.getMainDexRootSet() != null) {
+ appView.setMainDexRootSet(appView.getMainDexRootSet().rewrittenWithLens(lens));
+ }
});
}
diff --git a/src/main/java/com/android/tools/r8/graph/ClassAccessFlags.java b/src/main/java/com/android/tools/r8/graph/ClassAccessFlags.java
index 7998171..abeb404 100644
--- a/src/main/java/com/android/tools/r8/graph/ClassAccessFlags.java
+++ b/src/main/java/com/android/tools/r8/graph/ClassAccessFlags.java
@@ -22,9 +22,7 @@
private static final int DEX_FLAGS
= SHARED_FLAGS;
- private static final int CF_FLAGS
- = SHARED_FLAGS
- | Constants.ACC_SUPER;
+ private static final int CF_FLAGS = SHARED_FLAGS | Constants.ACC_SUPER | Constants.ACC_RECORD;
@Override
protected List<String> getNames() {
@@ -35,6 +33,7 @@
.add("annotation")
.add("enum")
.add("super")
+ .add("record")
.build();
}
@@ -47,6 +46,7 @@
.add(this::isAnnotation)
.add(this::isEnum)
.add(this::isSuper)
+ .add(this::isRecord)
.build();
}
@@ -178,6 +178,14 @@
set(Constants.ACC_ENUM);
}
+ public boolean isRecord() {
+ return isSet(Constants.ACC_RECORD);
+ }
+
+ public void setRecord() {
+ set(Constants.ACC_RECORD);
+ }
+
public boolean isSuper() {
return isSet(Constants.ACC_SUPER);
}
diff --git a/src/main/java/com/android/tools/r8/graph/ClasspathDefinition.java b/src/main/java/com/android/tools/r8/graph/ClasspathDefinition.java
new file mode 100644
index 0000000..89013fd
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/graph/ClasspathDefinition.java
@@ -0,0 +1,23 @@
+// Copyright (c) 2021, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.graph;
+
+import java.util.function.Function;
+
+public interface ClasspathDefinition extends Definition {
+
+ @Override
+ default <T> T apply(
+ Function<ProgramDefinition, T> programFunction,
+ Function<ClasspathDefinition, T> classpathFunction,
+ Function<LibraryDefinition, T> libraryFunction) {
+ return classpathFunction.apply(this);
+ }
+
+ @Override
+ default ProgramDerivedContext asProgramDerivedContext(ProgramDerivedContext witness) {
+ return ClasspathOrLibraryContext.create(this, witness);
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/graph/ClasspathField.java b/src/main/java/com/android/tools/r8/graph/ClasspathField.java
index b40e51c..ca7b15b 100644
--- a/src/main/java/com/android/tools/r8/graph/ClasspathField.java
+++ b/src/main/java/com/android/tools/r8/graph/ClasspathField.java
@@ -4,7 +4,8 @@
package com.android.tools.r8.graph;
-public class ClasspathField extends DexClassAndField {
+public class ClasspathField extends DexClassAndField
+ implements ClasspathMember<DexEncodedField, DexField> {
public ClasspathField(DexClasspathClass holder, DexEncodedField field) {
super(holder, field);
@@ -24,4 +25,11 @@
public boolean isClasspathMember() {
return true;
}
+
+ @Override
+ public DexClasspathClass getHolder() {
+ DexClass holder = super.getHolder();
+ assert holder.isClasspathClass();
+ return holder.asClasspathClass();
+ }
}
diff --git a/src/main/java/com/android/tools/r8/graph/ClasspathMember.java b/src/main/java/com/android/tools/r8/graph/ClasspathMember.java
new file mode 100644
index 0000000..8fad10f
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/graph/ClasspathMember.java
@@ -0,0 +1,15 @@
+// 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.graph;
+
+public interface ClasspathMember<D extends DexEncodedMember<D, R>, R extends DexMember<D, R>>
+ extends ClasspathDefinition {
+
+ D getDefinition();
+
+ DexClasspathClass getHolder();
+
+ DexType getHolderType();
+}
diff --git a/src/main/java/com/android/tools/r8/graph/ClasspathMethod.java b/src/main/java/com/android/tools/r8/graph/ClasspathMethod.java
index 0c58053..3f796e3 100644
--- a/src/main/java/com/android/tools/r8/graph/ClasspathMethod.java
+++ b/src/main/java/com/android/tools/r8/graph/ClasspathMethod.java
@@ -6,7 +6,8 @@
import com.android.tools.r8.logging.Log;
/** Type representing a method definition on the classpath and its holder. */
-public final class ClasspathMethod extends DexClassAndMethod {
+public final class ClasspathMethod extends DexClassAndMethod
+ implements ClasspathMember<DexEncodedMethod, DexMethod> {
public ClasspathMethod(DexClasspathClass holder, DexEncodedMethod method) {
super(holder, method);
diff --git a/src/main/java/com/android/tools/r8/graph/ClasspathOrLibraryContext.java b/src/main/java/com/android/tools/r8/graph/ClasspathOrLibraryContext.java
new file mode 100644
index 0000000..b498837
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/graph/ClasspathOrLibraryContext.java
@@ -0,0 +1,36 @@
+// Copyright (c) 2021, 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;
+
+/**
+ * A classpath or library definition that is guaranteed to be derived from the tracing of a program
+ * context.
+ */
+public class ClasspathOrLibraryContext implements ProgramDerivedContext {
+
+ private final Definition context;
+ private final ProgramDerivedContext programDerivedContext;
+
+ private ClasspathOrLibraryContext(
+ Definition context, ProgramDerivedContext programDerivedContext) {
+ this.context = context;
+ this.programDerivedContext = programDerivedContext;
+ }
+
+ public static ClasspathOrLibraryContext create(
+ ClasspathDefinition context, ProgramDerivedContext programDerivedContext) {
+ return new ClasspathOrLibraryContext(context, programDerivedContext);
+ }
+
+ public static ClasspathOrLibraryContext create(
+ LibraryDefinition context, ProgramDerivedContext programDerivedContext) {
+ return new ClasspathOrLibraryContext(context, programDerivedContext);
+ }
+
+ @Override
+ public Definition getContext() {
+ return context;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/graph/Definition.java b/src/main/java/com/android/tools/r8/graph/Definition.java
new file mode 100644
index 0000000..a7e4ed9
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/graph/Definition.java
@@ -0,0 +1,21 @@
+// Copyright (c) 2021, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.graph;
+
+import java.util.function.Function;
+
+public interface Definition {
+
+ <T> T apply(
+ Function<ProgramDefinition, T> programFunction,
+ Function<ClasspathDefinition, T> classpathFunction,
+ Function<LibraryDefinition, T> libraryFunction);
+
+ ProgramDerivedContext asProgramDerivedContext(ProgramDerivedContext witness);
+
+ DexType getContextType();
+
+ DexReference getReference();
+}
diff --git a/src/main/java/com/android/tools/r8/graph/DexClass.java b/src/main/java/com/android/tools/r8/graph/DexClass.java
index f1c0a2f..df80a80 100644
--- a/src/main/java/com/android/tools/r8/graph/DexClass.java
+++ b/src/main/java/com/android/tools/r8/graph/DexClass.java
@@ -33,7 +33,7 @@
import java.util.function.Function;
import java.util.function.Predicate;
-public abstract class DexClass extends DexDefinition {
+public abstract class DexClass extends DexDefinition implements Definition {
public interface FieldSetter {
void setField(int index, DexEncodedField field);
diff --git a/src/main/java/com/android/tools/r8/graph/DexClassAndField.java b/src/main/java/com/android/tools/r8/graph/DexClassAndField.java
index cef86d0..505a7ec 100644
--- a/src/main/java/com/android/tools/r8/graph/DexClassAndField.java
+++ b/src/main/java/com/android/tools/r8/graph/DexClassAndField.java
@@ -4,7 +4,7 @@
package com.android.tools.r8.graph;
-public class DexClassAndField extends DexClassAndMember<DexEncodedField, DexField> {
+public abstract class DexClassAndField extends DexClassAndMember<DexEncodedField, DexField> {
DexClassAndField(DexClass holder, DexEncodedField field) {
super(holder, field);
diff --git a/src/main/java/com/android/tools/r8/graph/DexClassAndMember.java b/src/main/java/com/android/tools/r8/graph/DexClassAndMember.java
index 83c4334..8459067 100644
--- a/src/main/java/com/android/tools/r8/graph/DexClassAndMember.java
+++ b/src/main/java/com/android/tools/r8/graph/DexClassAndMember.java
@@ -7,8 +7,8 @@
import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.origin.Origin;
-public abstract class DexClassAndMember<
- D extends DexEncodedMember<D, R>, R extends DexMember<D, R>> {
+public abstract class DexClassAndMember<D extends DexEncodedMember<D, R>, R extends DexMember<D, R>>
+ implements Definition {
private final DexClass holder;
private final D definition;
@@ -23,6 +23,7 @@
public abstract AccessFlags<?> getAccessFlags();
+ @Override
public DexType getContextType() {
return getHolderType();
}
@@ -43,6 +44,7 @@
return getReference().getName();
}
+ @Override
public R getReference() {
return definition.getReference();
}
diff --git a/src/main/java/com/android/tools/r8/graph/DexClassAndMethod.java b/src/main/java/com/android/tools/r8/graph/DexClassAndMethod.java
index 075c1ba..d944c02 100644
--- a/src/main/java/com/android/tools/r8/graph/DexClassAndMethod.java
+++ b/src/main/java/com/android/tools/r8/graph/DexClassAndMethod.java
@@ -4,7 +4,7 @@
package com.android.tools.r8.graph;
-public class DexClassAndMethod extends DexClassAndMember<DexEncodedMethod, DexMethod>
+public abstract class DexClassAndMethod extends DexClassAndMember<DexEncodedMethod, DexMethod>
implements LookupTarget {
DexClassAndMethod(DexClass holder, DexEncodedMethod method) {
diff --git a/src/main/java/com/android/tools/r8/graph/DexClasspathClass.java b/src/main/java/com/android/tools/r8/graph/DexClasspathClass.java
index 7fb6023..125155e 100644
--- a/src/main/java/com/android/tools/r8/graph/DexClasspathClass.java
+++ b/src/main/java/com/android/tools/r8/graph/DexClasspathClass.java
@@ -22,7 +22,7 @@
import java.util.function.Supplier;
public class DexClasspathClass extends DexClass
- implements Supplier<DexClasspathClass>, StructuralItem<DexClasspathClass> {
+ implements ClasspathDefinition, Supplier<DexClasspathClass>, StructuralItem<DexClasspathClass> {
public DexClasspathClass(
DexType type,
diff --git a/src/main/java/com/android/tools/r8/graph/DexEncodedField.java b/src/main/java/com/android/tools/r8/graph/DexEncodedField.java
index f933754..d15bfc7 100644
--- a/src/main/java/com/android/tools/r8/graph/DexEncodedField.java
+++ b/src/main/java/com/android/tools/r8/graph/DexEncodedField.java
@@ -18,6 +18,7 @@
import com.android.tools.r8.ir.optimize.info.DefaultFieldOptimizationInfo;
import com.android.tools.r8.ir.optimize.info.FieldOptimizationInfo;
import com.android.tools.r8.ir.optimize.info.MutableFieldOptimizationInfo;
+import com.android.tools.r8.ir.optimize.info.OptimizationFeedbackSimple;
import com.android.tools.r8.kotlin.KotlinFieldLevelInfo;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.android.tools.r8.utils.ConsumerUtils;
@@ -355,6 +356,7 @@
private FieldTypeSignature genericSignature;
private DexValue staticValue;
private FieldOptimizationInfo optimizationInfo;
+ private Consumer<DexEncodedField> buildConsumer = ConsumerUtils.emptyConsumer();
Builder(DexEncodedField from) {
// Copy all the mutable state of a DexEncodedField here.
@@ -370,15 +372,21 @@
: from.optimizationInfo.mutableCopy();
}
- public Builder fixupOptimizationInfo(Consumer<MutableFieldOptimizationInfo> consumer) {
- if (optimizationInfo.isMutableFieldOptimizationInfo()) {
- consumer.accept(optimizationInfo.asMutableFieldOptimizationInfo());
- }
+ public Builder apply(Consumer<Builder> consumer) {
+ consumer.accept(this);
return this;
}
- public Builder apply(Consumer<Builder> consumer) {
- consumer.accept(this);
+ public Builder setAbstractValue(
+ AbstractValue abstractValue, AppView<AppInfoWithLiveness> appView) {
+ return addBuildConsumer(
+ fixedUpField ->
+ OptimizationFeedbackSimple.getInstance()
+ .recordFieldHasAbstractValue(fixedUpField, appView, abstractValue));
+ }
+
+ private Builder addBuildConsumer(Consumer<DexEncodedField> consumer) {
+ this.buildConsumer = this.buildConsumer.andThen(consumer);
return this;
}
@@ -393,6 +401,7 @@
if (optimizationInfo.isMutableFieldOptimizationInfo()) {
dexEncodedField.setOptimizationInfo(optimizationInfo.asMutableFieldOptimizationInfo());
}
+ buildConsumer.accept(dexEncodedField);
return dexEncodedField;
}
}
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 c736831..c7a6e70 100644
--- a/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
+++ b/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
@@ -44,6 +44,7 @@
import com.android.tools.r8.errors.InternalCompilerError;
import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.graph.GenericSignature.MethodTypeSignature;
+import com.android.tools.r8.ir.analysis.inlining.SimpleInliningConstraint;
import com.android.tools.r8.ir.code.IRCode;
import com.android.tools.r8.ir.code.Invoke;
import com.android.tools.r8.ir.code.NumericType;
@@ -55,6 +56,7 @@
import com.android.tools.r8.ir.optimize.info.CallSiteOptimizationInfo;
import com.android.tools.r8.ir.optimize.info.DefaultMethodOptimizationInfo;
import com.android.tools.r8.ir.optimize.info.MethodOptimizationInfo;
+import com.android.tools.r8.ir.optimize.info.OptimizationFeedbackSimple;
import com.android.tools.r8.ir.optimize.info.UpdatableMethodOptimizationInfo;
import com.android.tools.r8.ir.optimize.inliner.WhyAreYouNotInliningReporter;
import com.android.tools.r8.ir.regalloc.RegisterAllocator;
@@ -72,6 +74,7 @@
import com.android.tools.r8.shaking.AnnotationRemover;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.android.tools.r8.utils.BooleanUtils;
+import com.android.tools.r8.utils.ConsumerUtils;
import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.OptionalBool;
import com.android.tools.r8.utils.Pair;
@@ -1536,6 +1539,7 @@
private KotlinMethodLevelInfo kotlinMemberInfo;
private final CfVersion classFileVersion;
private boolean d8R8Synthesized;
+ private Consumer<DexEncodedMethod> buildConsumer = ConsumerUtils.emptyConsumer();
private Builder(DexEncodedMethod from) {
this(from, from.d8R8Synthesized);
@@ -1567,10 +1571,19 @@
}
}
- public Builder fixupOptimizationInfo(Consumer<UpdatableMethodOptimizationInfo> consumer) {
- if (optimizationInfo.isUpdatableMethodOptimizationInfo()) {
- consumer.accept(optimizationInfo.asUpdatableMethodOptimizationInfo());
- }
+ public Builder setSimpleInliningConstraint(
+ DexProgramClass holder, SimpleInliningConstraint simpleInliningConstraint) {
+ return addBuildConsumer(
+ newMethod ->
+ OptimizationFeedbackSimple.getInstance()
+ .setSimpleInliningConstraint(
+ // The method has not yet been installed so we cannot use
+ // asProgramMethod(appView).
+ new ProgramMethod(holder, newMethod), simpleInliningConstraint));
+ }
+
+ private Builder addBuildConsumer(Consumer<DexEncodedMethod> consumer) {
+ this.buildConsumer = this.buildConsumer.andThen(consumer);
return this;
}
@@ -1699,6 +1712,7 @@
if (!isLibraryMethodOverride.isUnknown()) {
result.setLibraryMethodOverride(isLibraryMethodOverride);
}
+ buildConsumer.accept(result);
return result;
}
}
diff --git a/src/main/java/com/android/tools/r8/graph/DexItemFactory.java b/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
index 30ac84b..18b0434 100644
--- a/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
+++ b/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
@@ -25,7 +25,6 @@
import com.android.tools.r8.ir.analysis.type.TypeElement;
import com.android.tools.r8.ir.code.Position;
import com.android.tools.r8.ir.code.Value;
-import com.android.tools.r8.ir.desugar.nest.NestBasedAccessDesugaring;
import com.android.tools.r8.kotlin.Kotlin;
import com.android.tools.r8.utils.ArrayUtils;
import com.android.tools.r8.utils.DescriptorUtils;
@@ -214,6 +213,7 @@
public final DexString stringDescriptor = createString("Ljava/lang/String;");
public final DexString stringArrayDescriptor = createString("[Ljava/lang/String;");
public final DexString objectDescriptor = createString("Ljava/lang/Object;");
+ public final DexString recordDescriptor = createString("Ljava/lang/Record;");
public final DexString objectArrayDescriptor = createString("[Ljava/lang/Object;");
public final DexString classDescriptor = createString("Ljava/lang/Class;");
public final DexString classLoaderDescriptor = createString("Ljava/lang/ClassLoader;");
@@ -343,6 +343,7 @@
public final DexType stringType = createStaticallyKnownType(stringDescriptor);
public final DexType stringArrayType = createStaticallyKnownType(stringArrayDescriptor);
public final DexType objectType = createStaticallyKnownType(objectDescriptor);
+ public final DexType recordType = createStaticallyKnownType(recordDescriptor);
public final DexType objectArrayType = createStaticallyKnownType(objectArrayDescriptor);
public final DexType classArrayType = createStaticallyKnownType(classArrayDescriptor);
public final DexType enumType = createStaticallyKnownType(enumDescriptor);
@@ -424,6 +425,26 @@
createStaticallyKnownType(reflectiveOperationExceptionDescriptor);
public final DexType kotlinMetadataType = createStaticallyKnownType(kotlinMetadataDescriptor);
+ public final DexType doubleSummaryStatisticsConversionsType =
+ createStaticallyKnownType("Ljava/util/DoubleSummaryStatisticsConversions;");
+ public final DexType intSummaryStatisticsConversionsType =
+ createStaticallyKnownType("Ljava/util/IntSummaryStatisticsConversions;");
+ public final DexType longSummaryStatisticsConversionsType =
+ createStaticallyKnownType("Ljava/util/LongSummaryStatisticsConversions;");
+ public final DexType optionalConversionsType =
+ createStaticallyKnownType("Ljava/util/OptionalConversions;");
+ public final DexType timeConversionsType =
+ createStaticallyKnownType("Ljava/time/TimeConversions;");
+
+ public Iterable<DexType> getConversionTypes() {
+ return ImmutableList.of(
+ doubleSummaryStatisticsConversionsType,
+ intSummaryStatisticsConversionsType,
+ longSummaryStatisticsConversionsType,
+ optionalConversionsType,
+ timeConversionsType);
+ }
+
public final DexType javaIoFileType = createStaticallyKnownType("Ljava/io/File;");
public final DexType javaMathBigIntegerType = createStaticallyKnownType("Ljava/math/BigInteger;");
public final DexType javaNioByteOrderType = createStaticallyKnownType("Ljava/nio/ByteOrder;");
@@ -454,10 +475,6 @@
createStaticallyKnownType("Landroid/util/Property;");
public final DexType androidViewViewType = createStaticallyKnownType("Landroid/view/View;");
- public final DexString nestConstructorDescriptor =
- createString("L" + NestBasedAccessDesugaring.NEST_CONSTRUCTOR_NAME + ";");
- public final DexType nestConstructorType = createStaticallyKnownType(nestConstructorDescriptor);
-
public final StringBuildingMethods stringBuilderMethods =
new StringBuildingMethods(stringBuilderType);
public final StringBuildingMethods stringBufferMethods =
@@ -540,8 +557,10 @@
createProto(voidType, throwableType, autoCloseableType);
public final DexString deserializeLambdaMethodName = createString("$deserializeLambda$");
+ public final DexType serializedLambdaType =
+ createStaticallyKnownType("Ljava/lang/invoke/SerializedLambda;");
public final DexProto deserializeLambdaMethodProto =
- createProto(objectType, createStaticallyKnownType("Ljava/lang/invoke/SerializedLambda;"));
+ createProto(objectType, serializedLambdaType);
// Dex system annotations.
// See https://source.android.com/devices/tech/dalvik/dex-format.html#system-annotation
diff --git a/src/main/java/com/android/tools/r8/graph/DexLibraryClass.java b/src/main/java/com/android/tools/r8/graph/DexLibraryClass.java
index df36fb7..f34162c 100644
--- a/src/main/java/com/android/tools/r8/graph/DexLibraryClass.java
+++ b/src/main/java/com/android/tools/r8/graph/DexLibraryClass.java
@@ -17,7 +17,8 @@
import java.util.function.Predicate;
import java.util.function.Supplier;
-public class DexLibraryClass extends DexClass implements Supplier<DexLibraryClass> {
+public class DexLibraryClass extends DexClass
+ implements LibraryDefinition, Supplier<DexLibraryClass> {
public DexLibraryClass(
DexType type,
diff --git a/src/main/java/com/android/tools/r8/graph/DexProgramClass.java b/src/main/java/com/android/tools/r8/graph/DexProgramClass.java
index 618ff5b..0fdd6ad 100644
--- a/src/main/java/com/android/tools/r8/graph/DexProgramClass.java
+++ b/src/main/java/com/android/tools/r8/graph/DexProgramClass.java
@@ -161,6 +161,11 @@
}
@Override
+ public DexProgramClass getContext() {
+ return this;
+ }
+
+ @Override
public StructuralMapping<DexProgramClass> getStructuralMapping() {
return DexProgramClass::specify;
}
@@ -801,7 +806,7 @@
private DexProgramClass findNext() {
while (iterator.hasNext()) {
DexType next = iterator.next();
- DexClass clazz = definitions.definitionFor(next);
+ DexClass clazz = definitions.contextIndependentDefinitionFor(next);
if (clazz != null && clazz.isProgramClass()) {
return clazz.asProgramClass();
}
diff --git a/src/main/java/com/android/tools/r8/graph/DexType.java b/src/main/java/com/android/tools/r8/graph/DexType.java
index 76d2ea4..f806886 100644
--- a/src/main/java/com/android/tools/r8/graph/DexType.java
+++ b/src/main/java/com/android/tools/r8/graph/DexType.java
@@ -4,11 +4,11 @@
package com.android.tools.r8.graph;
import static com.android.tools.r8.graph.DexProgramClass.asProgramClassOrNull;
-import static com.android.tools.r8.ir.desugar.LambdaRewriter.LAMBDA_GROUP_CLASS_NAME_PREFIX;
import com.android.tools.r8.dex.IndexedItemCollection;
import com.android.tools.r8.errors.Unreachable;
-import com.android.tools.r8.ir.desugar.nest.NestBasedAccessDesugaring;
+import com.android.tools.r8.references.ClassReference;
+import com.android.tools.r8.references.Reference;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.android.tools.r8.utils.DescriptorUtils;
import com.android.tools.r8.utils.structural.CompareToVisitor;
@@ -36,7 +36,9 @@
"$-DC",
"$$ServiceLoaderMethods",
"com.android.tools.r8.GeneratedOutlineSupport",
- "-$$Lambda$");
+ "-$$Nest$Constructor",
+ "-$$Lambda$",
+ "-$$LambdaGroup$");
public final DexString descriptor;
private String toStringCache = null;
@@ -46,6 +48,10 @@
this.descriptor = descriptor;
}
+ public ClassReference asClassReference() {
+ return Reference.classFromDescriptor(toDescriptorString());
+ }
+
@Override
public DexType self() {
return this;
@@ -301,15 +307,7 @@
}
public boolean isLegacySynthesizedTypeAllowedDuplication() {
- String name = toSourceString();
- return isSynthesizedTypeThatCouldBeDuplicated(name) || oldSynthesizedName(name);
- }
-
- private static boolean isSynthesizedTypeThatCouldBeDuplicated(String name) {
- // Any entry that is removed from here must be added to OLD_SYNTHESIZED_NAMES to ensure that
- // newer releases can be used to merge previous builds.
- return name.contains(LAMBDA_GROUP_CLASS_NAME_PREFIX) // Could collide.
- || name.contains(NestBasedAccessDesugaring.NEST_CONSTRUCTOR_NAME); // Global singleton.
+ return oldSynthesizedName(toSourceString());
}
private boolean oldSynthesizedName(String name) {
diff --git a/src/main/java/com/android/tools/r8/graph/FieldResolutionResult.java b/src/main/java/com/android/tools/r8/graph/FieldResolutionResult.java
index 4483fc5..8b1ca9d 100644
--- a/src/main/java/com/android/tools/r8/graph/FieldResolutionResult.java
+++ b/src/main/java/com/android/tools/r8/graph/FieldResolutionResult.java
@@ -25,6 +25,10 @@
return null;
}
+ public DexClassAndField getResolutionPair() {
+ return null;
+ }
+
public boolean isSuccessfulResolution() {
return false;
}
diff --git a/src/main/java/com/android/tools/r8/graph/GraphLens.java b/src/main/java/com/android/tools/r8/graph/GraphLens.java
index 2f6c224..56d3f52 100644
--- a/src/main/java/com/android/tools/r8/graph/GraphLens.java
+++ b/src/main/java/com/android/tools/r8/graph/GraphLens.java
@@ -300,6 +300,10 @@
*/
private GraphLens() {}
+ public boolean isSyntheticFinalizationGraphLens() {
+ return false;
+ }
+
public abstract DexType getOriginalType(DexType type);
public abstract Iterable<DexType> getOriginalTypes(DexType type);
@@ -413,14 +417,7 @@
}
public DexReference lookupReference(DexReference reference) {
- if (reference.isDexType()) {
- return lookupType(reference.asDexType());
- } else if (reference.isDexMethod()) {
- return lookupMethod(reference.asDexMethod());
- } else {
- assert reference.isDexField();
- return lookupField(reference.asDexField());
- }
+ return reference.apply(this::lookupType, this::lookupField, this::lookupMethod);
}
// The method lookupMethod() maps a pair INVOKE=(method signature, invoke type) to a new pair
@@ -532,14 +529,9 @@
@SuppressWarnings("unchecked")
public <T extends DexReference> T rewriteReference(T reference) {
- if (reference.isDexField()) {
- return (T) getRenamedFieldSignature(reference.asDexField());
- }
- if (reference.isDexMethod()) {
- return (T) getRenamedMethodSignature(reference.asDexMethod());
- }
- assert reference.isDexType();
- return (T) lookupType(reference.asDexType());
+ return (T)
+ reference.apply(
+ this::lookupType, this::getRenamedFieldSignature, this::getRenamedMethodSignature);
}
public Set<DexReference> rewriteReferences(Set<DexReference> references) {
diff --git a/src/main/java/com/android/tools/r8/graph/JarClassFileReader.java b/src/main/java/com/android/tools/r8/graph/JarClassFileReader.java
index 40b6445..1f67c4d 100644
--- a/src/main/java/com/android/tools/r8/graph/JarClassFileReader.java
+++ b/src/main/java/com/android/tools/r8/graph/JarClassFileReader.java
@@ -301,7 +301,8 @@
@Override
public RecordComponentVisitor visitRecordComponent(
String name, String descriptor, String signature) {
- throw new CompilationError("Records are not supported", origin);
+ // TODO(b/169645628): Support Records.
+ return super.visitRecordComponent(name, descriptor, signature);
}
@Override
@@ -326,6 +327,12 @@
}
this.deprecated = AsmUtils.isDeprecated(access);
accessFlags = ClassAccessFlags.fromCfAccessFlags(cleanAccessFlags(access));
+ if (accessFlags.isRecord()) {
+ // TODO(b/169645628): Support records in all compilation.
+ if (!application.options.canUseRecords()) {
+ throw new CompilationError("Records are not supported", origin);
+ }
+ }
type = application.getTypeFromName(name);
// Check if constraints from
// https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-4.html#jvms-4.1 are met.
diff --git a/src/main/java/com/android/tools/r8/graph/LibraryDefinition.java b/src/main/java/com/android/tools/r8/graph/LibraryDefinition.java
new file mode 100644
index 0000000..a2fb6a8
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/graph/LibraryDefinition.java
@@ -0,0 +1,23 @@
+// Copyright (c) 2021, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.graph;
+
+import java.util.function.Function;
+
+public interface LibraryDefinition extends Definition {
+
+ @Override
+ default <T> T apply(
+ Function<ProgramDefinition, T> programFunction,
+ Function<ClasspathDefinition, T> classpathFunction,
+ Function<LibraryDefinition, T> libraryFunction) {
+ return libraryFunction.apply(this);
+ }
+
+ @Override
+ default ProgramDerivedContext asProgramDerivedContext(ProgramDerivedContext witness) {
+ return ClasspathOrLibraryContext.create(this, witness);
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/graph/LibraryMember.java b/src/main/java/com/android/tools/r8/graph/LibraryMember.java
index 880da78..bf6bb03 100644
--- a/src/main/java/com/android/tools/r8/graph/LibraryMember.java
+++ b/src/main/java/com/android/tools/r8/graph/LibraryMember.java
@@ -4,7 +4,8 @@
package com.android.tools.r8.graph;
-public interface LibraryMember<D extends DexEncodedMember<D, R>, R extends DexMember<D, R>> {
+public interface LibraryMember<D extends DexEncodedMember<D, R>, R extends DexMember<D, R>>
+ extends LibraryDefinition {
D getDefinition();
diff --git a/src/main/java/com/android/tools/r8/graph/ProgramDefinition.java b/src/main/java/com/android/tools/r8/graph/ProgramDefinition.java
index c55ad6f..862efbc 100644
--- a/src/main/java/com/android/tools/r8/graph/ProgramDefinition.java
+++ b/src/main/java/com/android/tools/r8/graph/ProgramDefinition.java
@@ -5,19 +5,29 @@
package com.android.tools.r8.graph;
import com.android.tools.r8.origin.Origin;
+import java.util.function.Function;
-public interface ProgramDefinition {
+public interface ProgramDefinition extends Definition, ProgramDerivedContext {
+
+ @Override
+ default <T> T apply(
+ Function<ProgramDefinition, T> programFunction,
+ Function<ClasspathDefinition, T> classpathFunction,
+ Function<LibraryDefinition, T> libraryFunction) {
+ return programFunction.apply(this);
+ }
+
+ @Override
+ default ProgramDerivedContext asProgramDerivedContext(ProgramDerivedContext witness) {
+ return this;
+ }
DexProgramClass getContextClass();
AccessFlags<?> getAccessFlags();
- DexType getContextType();
-
DexDefinition getDefinition();
- DexReference getReference();
-
Origin getOrigin();
default boolean isProgramClass() {
diff --git a/src/main/java/com/android/tools/r8/graph/ProgramDerivedContext.java b/src/main/java/com/android/tools/r8/graph/ProgramDerivedContext.java
new file mode 100644
index 0000000..f9ec920
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/graph/ProgramDerivedContext.java
@@ -0,0 +1,10 @@
+// Copyright (c) 2021, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.graph;
+
+public interface ProgramDerivedContext {
+
+ Definition getContext();
+}
diff --git a/src/main/java/com/android/tools/r8/graph/ProgramField.java b/src/main/java/com/android/tools/r8/graph/ProgramField.java
index 866d230..42d2453 100644
--- a/src/main/java/com/android/tools/r8/graph/ProgramField.java
+++ b/src/main/java/com/android/tools/r8/graph/ProgramField.java
@@ -27,6 +27,11 @@
}
@Override
+ public ProgramField getContext() {
+ return this;
+ }
+
+ @Override
public boolean isProgramField() {
return true;
}
diff --git a/src/main/java/com/android/tools/r8/graph/ProgramMethod.java b/src/main/java/com/android/tools/r8/graph/ProgramMethod.java
index 3dc807c..a8817dc 100644
--- a/src/main/java/com/android/tools/r8/graph/ProgramMethod.java
+++ b/src/main/java/com/android/tools/r8/graph/ProgramMethod.java
@@ -63,6 +63,11 @@
}
@Override
+ public ProgramMethod getContext() {
+ return this;
+ }
+
+ @Override
public boolean isProgramMember() {
return true;
}
diff --git a/src/main/java/com/android/tools/r8/graph/classmerging/HorizontallyMergedLambdaClasses.java b/src/main/java/com/android/tools/r8/graph/classmerging/HorizontallyMergedLambdaClasses.java
deleted file mode 100644
index 3a9f618..0000000
--- a/src/main/java/com/android/tools/r8/graph/classmerging/HorizontallyMergedLambdaClasses.java
+++ /dev/null
@@ -1,59 +0,0 @@
-// Copyright (c) 2019, the R8 project authors. Please see the AUTHORS file
-// for details. All rights reserved. Use of this source code is governed by a
-// BSD-style license that can be found in the LICENSE file.
-
-package com.android.tools.r8.graph.classmerging;
-
-import com.android.tools.r8.graph.AppView;
-import com.android.tools.r8.graph.DexType;
-import com.android.tools.r8.ir.optimize.lambda.LambdaGroup;
-import com.android.tools.r8.shaking.AppInfoWithLiveness;
-import com.android.tools.r8.utils.collections.BidirectionalManyToOneHashMap;
-import com.android.tools.r8.utils.collections.BidirectionalManyToOneMap;
-import com.android.tools.r8.utils.collections.MutableBidirectionalManyToOneMap;
-import java.util.Collections;
-import java.util.Map;
-import java.util.Set;
-import java.util.function.BiConsumer;
-
-public class HorizontallyMergedLambdaClasses implements MergedClasses {
-
- private final BidirectionalManyToOneMap<DexType, DexType> mergedClasses;
-
- public HorizontallyMergedLambdaClasses(Map<DexType, LambdaGroup> lambdas) {
- MutableBidirectionalManyToOneMap<DexType, DexType> mergedClasses =
- new BidirectionalManyToOneHashMap<>();
- lambdas.forEach((lambda, group) -> mergedClasses.put(lambda, group.getGroupClassType()));
- this.mergedClasses = mergedClasses;
- }
-
- public static HorizontallyMergedLambdaClasses empty() {
- return new HorizontallyMergedLambdaClasses(Collections.emptyMap());
- }
-
- @Override
- public void forEachMergeGroup(BiConsumer<Set<DexType>, DexType> consumer) {
- mergedClasses.forEachManyToOneMapping(consumer);
- }
-
- @Override
- public boolean hasBeenMergedIntoDifferentType(DexType type) {
- return mergedClasses.containsKey(type);
- }
-
- @Override
- public boolean isMergeTarget(DexType type) {
- return mergedClasses.containsValue(type);
- }
-
- @Override
- public boolean verifyAllSourcesPruned(AppView<AppInfoWithLiveness> appView) {
- for (DexType source : mergedClasses.keySet()) {
- assert appView.appInfo().wasPruned(source)
- : "Expected horizontally merged lambda class `"
- + source.toSourceString()
- + "` to be absent";
- }
- return true;
- }
-}
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontalClassMerger.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontalClassMerger.java
index dd0486a..d97f12f 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontalClassMerger.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontalClassMerger.java
@@ -23,14 +23,14 @@
import com.android.tools.r8.horizontalclassmerging.policies.NoInnerClasses;
import com.android.tools.r8.horizontalclassmerging.policies.NoInterfaces;
import com.android.tools.r8.horizontalclassmerging.policies.NoKeepRules;
-import com.android.tools.r8.horizontalclassmerging.policies.NoKotlinLambdas;
import com.android.tools.r8.horizontalclassmerging.policies.NoKotlinMetadata;
import com.android.tools.r8.horizontalclassmerging.policies.NoNativeMethods;
import com.android.tools.r8.horizontalclassmerging.policies.NoServiceLoaders;
import com.android.tools.r8.horizontalclassmerging.policies.NotMatchedByNoHorizontalClassMerging;
import com.android.tools.r8.horizontalclassmerging.policies.NotVerticallyMergedIntoSubtype;
import com.android.tools.r8.horizontalclassmerging.policies.PreserveMethodCharacteristics;
-import com.android.tools.r8.horizontalclassmerging.policies.PreventMergeIntoMainDex;
+import com.android.tools.r8.horizontalclassmerging.policies.PreventMergeIntoDifferentMainDexGroups;
+import com.android.tools.r8.horizontalclassmerging.policies.PreventMergeIntoMainDexList;
import com.android.tools.r8.horizontalclassmerging.policies.PreventMethodImplementation;
import com.android.tools.r8.horizontalclassmerging.policies.RespectPackageBoundaries;
import com.android.tools.r8.horizontalclassmerging.policies.SameFeatureSplit;
@@ -40,7 +40,6 @@
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.android.tools.r8.shaking.FieldAccessInfoCollectionModifier;
import com.android.tools.r8.shaking.KeepInfoCollection;
-import com.android.tools.r8.shaking.MainDexTracingResult;
import com.android.tools.r8.shaking.RuntimeTypeCheckInfo;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Iterables;
@@ -61,12 +60,11 @@
// TODO(b/165577835): replace Collection<DexProgramClass> with MergeGroup
public HorizontalClassMergerResult run(
DirectMappedDexApplication.Builder appBuilder,
- MainDexTracingResult mainDexTracingResult,
RuntimeTypeCheckInfo runtimeTypeCheckInfo) {
MergeGroup initialGroup = new MergeGroup(appView.appInfo().classesWithDeterministicOrder());
// Run the policies on all program classes to produce a final grouping.
- List<Policy> policies = getPolicies(mainDexTracingResult, runtimeTypeCheckInfo);
+ List<Policy> policies = getPolicies(runtimeTypeCheckInfo);
Collection<MergeGroup> groups =
new SimplePolicyExecutor().run(Collections.singletonList(initialGroup), policies);
@@ -80,8 +78,6 @@
new HorizontallyMergedClasses.Builder();
HorizontalClassMergerGraphLens.Builder lensBuilder =
new HorizontalClassMergerGraphLens.Builder();
- MainDexTracingResult.Builder mainDexTracingResultBuilder =
- mainDexTracingResult.extensionBuilder(appView.appInfo());
// Set up a class merger for each group.
List<ClassMerger> classMergers =
@@ -92,9 +88,7 @@
// Merge the classes.
SyntheticArgumentClass syntheticArgumentClass =
- new SyntheticArgumentClass.Builder(
- appBuilder, appView, mainDexTracingResult, mainDexTracingResultBuilder)
- .build(allMergeClasses);
+ new SyntheticArgumentClass.Builder(appBuilder, appView).build(allMergeClasses);
applyClassMergers(classMergers, syntheticArgumentClass);
// Generate the graph lens.
@@ -107,8 +101,7 @@
KeepInfoCollection keepInfo = appView.appInfo().getKeepInfo();
keepInfo.mutate(mutator -> mutator.removeKeepInfoForPrunedItems(mergedClasses.getSources()));
- return new HorizontalClassMergerResult(
- createFieldAccessInfoCollectionModifier(groups), lens, mainDexTracingResultBuilder.build());
+ return new HorizontalClassMergerResult(createFieldAccessInfoCollectionModifier(groups), lens);
}
private FieldAccessInfoCollectionModifier createFieldAccessInfoCollectionModifier(
@@ -127,9 +120,7 @@
return builder.build();
}
- private List<Policy> getPolicies(
- MainDexTracingResult mainDexTracingResult,
- RuntimeTypeCheckInfo runtimeTypeCheckInfo) {
+ private List<Policy> getPolicies(RuntimeTypeCheckInfo runtimeTypeCheckInfo) {
return ImmutableList.of(
new NotMatchedByNoHorizontalClassMerging(appView),
new SameInstanceFields(appView),
@@ -138,20 +129,20 @@
new NoEnums(appView),
new CheckAbstractClasses(appView),
new IgnoreSynthetics(appView),
- new NoClassesOrMembersWithAnnotations(),
+ new NoClassesOrMembersWithAnnotations(appView),
new NoInnerClasses(),
new NoClassInitializerWithObservableSideEffects(),
new NoNativeMethods(),
new NoKeepRules(appView),
new NoKotlinMetadata(),
- new NoKotlinLambdas(appView),
new NoServiceLoaders(appView),
new NotVerticallyMergedIntoSubtype(appView),
new NoDirectRuntimeTypeChecks(runtimeTypeCheckInfo),
new NoIndirectRuntimeTypeChecks(appView, runtimeTypeCheckInfo),
new PreventMethodImplementation(appView),
- new DontInlinePolicy(appView, mainDexTracingResult),
- new PreventMergeIntoMainDex(appView, mainDexTracingResult),
+ new DontInlinePolicy(appView),
+ new PreventMergeIntoMainDexList(appView),
+ new PreventMergeIntoDifferentMainDexGroups(appView),
new AllInstantiatedOrUninstantiated(appView),
new SameParentClass(),
new SameNestHost(),
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontalClassMergerResult.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontalClassMergerResult.java
index 75aafb4..820c24a 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontalClassMergerResult.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontalClassMergerResult.java
@@ -5,21 +5,17 @@
package com.android.tools.r8.horizontalclassmerging;
import com.android.tools.r8.shaking.FieldAccessInfoCollectionModifier;
-import com.android.tools.r8.shaking.MainDexTracingResult;
public class HorizontalClassMergerResult {
private final FieldAccessInfoCollectionModifier fieldAccessInfoCollectionModifier;
private final HorizontalClassMergerGraphLens graphLens;
- private final MainDexTracingResult mainDexTracingResult;
HorizontalClassMergerResult(
FieldAccessInfoCollectionModifier fieldAccessInfoCollectionModifier,
- HorizontalClassMergerGraphLens graphLens,
- MainDexTracingResult mainDexTracingResult) {
+ HorizontalClassMergerGraphLens graphLens) {
this.fieldAccessInfoCollectionModifier = fieldAccessInfoCollectionModifier;
this.graphLens = graphLens;
- this.mainDexTracingResult = mainDexTracingResult;
}
public FieldAccessInfoCollectionModifier getFieldAccessInfoCollectionModifier() {
@@ -29,8 +25,4 @@
public HorizontalClassMergerGraphLens getGraphLens() {
return graphLens;
}
-
- public MainDexTracingResult getMainDexTracingResult() {
- return mainDexTracingResult;
- }
}
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/SyntheticArgumentClass.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/SyntheticArgumentClass.java
index ec64e8a..60c350a 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/SyntheticArgumentClass.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/SyntheticArgumentClass.java
@@ -17,7 +17,7 @@
import com.android.tools.r8.graph.GenericSignature.ClassSignature;
import com.android.tools.r8.origin.SynthesizedOrigin;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
-import com.android.tools.r8.shaking.MainDexTracingResult;
+import com.android.tools.r8.shaking.MainDexInfo;
import com.google.common.collect.Iterables;
import java.util.ArrayList;
import java.util.Collections;
@@ -57,24 +57,15 @@
private final DirectMappedDexApplication.Builder appBuilder;
private final AppView<AppInfoWithLiveness> appView;
- private final MainDexTracingResult mainDexTracingResult;
- private final MainDexTracingResult.Builder mainDexTracingResultBuilder;
- Builder(
- DirectMappedDexApplication.Builder appBuilder,
- AppView<AppInfoWithLiveness> appView,
- MainDexTracingResult mainDexTracingResult,
- MainDexTracingResult.Builder mainDexTracingResultBuilder) {
+ Builder(DirectMappedDexApplication.Builder appBuilder, AppView<AppInfoWithLiveness> appView) {
this.appBuilder = appBuilder;
this.appView = appView;
- this.mainDexTracingResult = mainDexTracingResult;
- this.mainDexTracingResultBuilder = mainDexTracingResultBuilder;
}
private DexType synthesizeClass(
DexProgramClass context,
boolean requiresMainDex,
- boolean addToMainDexTracingResult,
int index) {
DexType syntheticClassType =
appView
@@ -108,10 +99,6 @@
appBuilder.addSynthesizedClass(clazz);
appView.appInfo().addSynthesizedClass(clazz, requiresMainDex);
- if (addToMainDexTracingResult) {
- mainDexTracingResultBuilder.addRoot(clazz);
- }
-
return clazz.type;
}
@@ -119,20 +106,17 @@
// Find a fresh name in an existing package.
DexProgramClass context = mergeClasses.iterator().next();
- // Add to the main dex list if one of the merged classes is in the main dex.
- boolean requiresMainDex = appView.appInfo().getMainDexClasses().containsAnyOf(mergeClasses);
-
- // Also add as a root to the main dex tracing result if any of the merged classes is a root.
+ // Add as a root to the main dex tracing result if any of the merged classes is a root.
// This is needed to satisfy an assertion in the inliner that verifies that we do not inline
// methods with references to non-roots into classes that are roots.
- boolean addToMainDexTracingResult = Iterables.any(mergeClasses, mainDexTracingResult::isRoot);
+ MainDexInfo mainDexInfo = appView.appInfo().getMainDexInfo();
+ boolean requiresMainDex = Iterables.any(mergeClasses, mainDexInfo::isMainDex);
List<DexType> syntheticArgumentTypes = new ArrayList<>();
for (int i = 0;
i < appView.options().horizontalClassMergerOptions().getSyntheticArgumentCount();
i++) {
- syntheticArgumentTypes.add(
- synthesizeClass(context, requiresMainDex, addToMainDexTracingResult, i));
+ syntheticArgumentTypes.add(synthesizeClass(context, requiresMainDex, i));
}
return new SyntheticArgumentClass(syntheticArgumentTypes);
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/DontInlinePolicy.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/DontInlinePolicy.java
index 9612f98..f7d121a 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/DontInlinePolicy.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/DontInlinePolicy.java
@@ -12,18 +12,16 @@
import com.android.tools.r8.horizontalclassmerging.SingleClassPolicy;
import com.android.tools.r8.ir.optimize.Inliner.ConstraintWithTarget;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
-import com.android.tools.r8.shaking.MainDexDirectReferenceTracer;
-import com.android.tools.r8.shaking.MainDexTracingResult;
+import com.android.tools.r8.shaking.MainDexInfo;
import com.google.common.collect.Iterables;
public class DontInlinePolicy extends SingleClassPolicy {
private final AppView<AppInfoWithLiveness> appView;
- private final MainDexTracingResult mainDexTracingResult;
+ private final MainDexInfo mainDexInfo;
- public DontInlinePolicy(
- AppView<AppInfoWithLiveness> appView, MainDexTracingResult mainDexTracingResult) {
+ public DontInlinePolicy(AppView<AppInfoWithLiveness> appView) {
this.appView = appView;
- this.mainDexTracingResult = mainDexTracingResult;
+ this.mainDexInfo = appView.appInfo().getMainDexInfo();
}
private boolean disallowInlining(ProgramMethod method) {
@@ -46,14 +44,6 @@
return true;
}
- // Constructors can have references beyond the root main dex classes. This can increase the
- // size of the main dex dependent classes and we should bail out.
- if (mainDexTracingResult.getRoots().contains(method.getHolderType())
- && MainDexDirectReferenceTracer.hasReferencesOutsideFromCode(
- appView.appInfo(), method, mainDexTracingResult.getRoots())) {
- return true;
- }
-
return false;
}
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoClassesOrMembersWithAnnotations.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoClassesOrMembersWithAnnotations.java
index f9d2106..f0b2a6e 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoClassesOrMembersWithAnnotations.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoClassesOrMembersWithAnnotations.java
@@ -4,12 +4,28 @@
package com.android.tools.r8.horizontalclassmerging.policies;
+import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.horizontalclassmerging.SingleClassPolicy;
+import com.android.tools.r8.shaking.AppInfoWithLiveness;
+import com.android.tools.r8.utils.InternalOptions.HorizontalClassMergerOptions;
public class NoClassesOrMembersWithAnnotations extends SingleClassPolicy {
+
+ private final HorizontalClassMergerOptions options;
+
+ public NoClassesOrMembersWithAnnotations(AppView<AppInfoWithLiveness> appView) {
+ this.options = appView.options().horizontalClassMergerOptions();
+ }
+
@Override
public boolean canMerge(DexProgramClass program) {
return !program.hasClassOrMemberAnnotations();
}
+
+ @Override
+ public boolean shouldSkipPolicy() {
+ // TODO(b/179019716): Add support for merging in presence of annotations.
+ return options.skipNoClassesOrMembersWithAnnotationsPolicyForTesting;
+ }
}
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoInnerClasses.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoInnerClasses.java
index ddb2aef..cc9f61c 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoInnerClasses.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoInnerClasses.java
@@ -11,6 +11,7 @@
@Override
public boolean canMerge(DexProgramClass program) {
+ // TODO(b/179018501): allow merging classes with inner/outer classes.
return program.getInnerClasses().isEmpty();
}
}
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoKotlinLambdas.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoKotlinLambdas.java
deleted file mode 100644
index dee7a4a..0000000
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoKotlinLambdas.java
+++ /dev/null
@@ -1,33 +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.horizontalclassmerging.policies;
-
-import com.android.tools.r8.graph.AppView;
-import com.android.tools.r8.graph.DexProgramClass;
-import com.android.tools.r8.horizontalclassmerging.SingleClassPolicy;
-import com.android.tools.r8.shaking.AppInfoWithLiveness;
-
-public class NoKotlinLambdas extends SingleClassPolicy {
- private final AppView<AppInfoWithLiveness> appView;
-
- public NoKotlinLambdas(AppView<AppInfoWithLiveness> appView) {
- this.appView = appView;
- }
-
- @Override
- public boolean shouldSkipPolicy() {
- return appView.options().horizontalClassMergerOptions().isKotlinLambdaMergingEnabled();
- }
-
- @Override
- public boolean canMerge(DexProgramClass program) {
- if (program.getKotlinInfo().isNoKotlinInformation()
- || !program.getKotlinInfo().isSyntheticClass()) {
- return true;
- }
-
- return !program.getKotlinInfo().asSyntheticClass().isLambda();
- }
-}
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoKotlinMetadata.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoKotlinMetadata.java
index 92f2938..1acad7a 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoKotlinMetadata.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoKotlinMetadata.java
@@ -19,12 +19,8 @@
}
private boolean verifyNoUnexpectedKotlinInfo(DexProgramClass clazz) {
- if (clazz.getKotlinInfo().isNoKotlinInformation()) {
- assert verifyNoUnexpectedKotlinMemberInfo(clazz);
- return true;
- }
- assert clazz.getKotlinInfo().isSyntheticClass()
- && clazz.getKotlinInfo().asSyntheticClass().isLambda();
+ assert clazz.getKotlinInfo().isNoKotlinInformation();
+ assert verifyNoUnexpectedKotlinMemberInfo(clazz);
return true;
}
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/PreserveMethodCharacteristics.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/PreserveMethodCharacteristics.java
index fcafba0..4a789ba 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/PreserveMethodCharacteristics.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/PreserveMethodCharacteristics.java
@@ -20,6 +20,7 @@
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
+import java.util.Objects;
/**
* Policy that enforces that methods are only merged if they have the same visibility and library
@@ -32,8 +33,10 @@
private final MethodAccessFlags accessFlags;
private final boolean isAssumeNoSideEffectsMethod;
private final OptionalBool isLibraryMethodOverride;
+ private final boolean isMainDexRoot;
- private MethodCharacteristics(boolean isAssumeNoSideEffectsMethod, DexEncodedMethod method) {
+ private MethodCharacteristics(
+ DexEncodedMethod method, boolean isAssumeNoSideEffectsMethod, boolean isMainDexRoot) {
this.accessFlags =
MethodAccessFlags.builder()
.setPrivate(method.getAccessFlags().isPrivate())
@@ -44,17 +47,24 @@
.build();
this.isAssumeNoSideEffectsMethod = isAssumeNoSideEffectsMethod;
this.isLibraryMethodOverride = method.isLibraryMethodOverride();
+ this.isMainDexRoot = isMainDexRoot;
}
static MethodCharacteristics create(
AppView<AppInfoWithLiveness> appView, DexEncodedMethod method) {
return new MethodCharacteristics(
- appView.appInfo().isAssumeNoSideEffectsMethod(method.getReference()), method);
+ method,
+ appView.appInfo().isAssumeNoSideEffectsMethod(method.getReference()),
+ appView.appInfo().getMainDexInfo().isTracedMethodRoot(method.getReference()));
}
@Override
public int hashCode() {
- return (accessFlags.hashCode() << 2) | isLibraryMethodOverride.ordinal();
+ return Objects.hash(
+ accessFlags,
+ isAssumeNoSideEffectsMethod,
+ isLibraryMethodOverride.ordinal(),
+ isMainDexRoot);
}
@Override
@@ -68,7 +78,8 @@
MethodCharacteristics characteristics = (MethodCharacteristics) obj;
return accessFlags.equals(characteristics.accessFlags)
&& isAssumeNoSideEffectsMethod == characteristics.isAssumeNoSideEffectsMethod
- && isLibraryMethodOverride == characteristics.isLibraryMethodOverride;
+ && isLibraryMethodOverride == characteristics.isLibraryMethodOverride
+ && isMainDexRoot == characteristics.isMainDexRoot;
}
}
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/PreventMergeIntoDifferentMainDexGroups.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/PreventMergeIntoDifferentMainDexGroups.java
new file mode 100644
index 0000000..52a1d98
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/PreventMergeIntoDifferentMainDexGroups.java
@@ -0,0 +1,28 @@
+// 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.horizontalclassmerging.policies;
+
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.horizontalclassmerging.MultiClassSameReferencePolicy;
+import com.android.tools.r8.shaking.AppInfoWithLiveness;
+import com.android.tools.r8.shaking.MainDexInfo;
+import com.android.tools.r8.shaking.MainDexInfo.MainDexGroup;
+
+public class PreventMergeIntoDifferentMainDexGroups
+ extends MultiClassSameReferencePolicy<MainDexGroup> {
+
+ private final MainDexInfo mainDexInfo;
+
+ public PreventMergeIntoDifferentMainDexGroups(AppView<AppInfoWithLiveness> appView) {
+ this.mainDexInfo = appView.appInfo().getMainDexInfo();
+ }
+
+ @Override
+ public MainDexGroup getMergeKey(DexProgramClass clazz) {
+ assert !mainDexInfo.isFromList(clazz);
+ return mainDexInfo.getMergeKey(clazz);
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/PreventMergeIntoMainDex.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/PreventMergeIntoMainDex.java
deleted file mode 100644
index 783ede5..0000000
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/PreventMergeIntoMainDex.java
+++ /dev/null
@@ -1,45 +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.horizontalclassmerging.policies;
-
-import com.android.tools.r8.graph.AppView;
-import com.android.tools.r8.graph.DexProgramClass;
-import com.android.tools.r8.horizontalclassmerging.MultiClassSameReferencePolicy;
-import com.android.tools.r8.horizontalclassmerging.policies.PreventMergeIntoMainDex.MainDexClassification;
-import com.android.tools.r8.shaking.AppInfoWithLiveness;
-import com.android.tools.r8.shaking.MainDexClasses;
-import com.android.tools.r8.shaking.MainDexTracingResult;
-
-public class PreventMergeIntoMainDex extends MultiClassSameReferencePolicy<MainDexClassification> {
- private final MainDexClasses mainDexClasses;
- private final MainDexTracingResult mainDexTracingResult;
-
- enum MainDexClassification {
- MAIN_DEX_LIST,
- MAIN_DEX_ROOT,
- MAIN_DEX_DEPENDENCY,
- NOT_IN_MAIN_DEX
- }
-
- public PreventMergeIntoMainDex(
- AppView<AppInfoWithLiveness> appView, MainDexTracingResult mainDexTracingResult) {
- this.mainDexClasses = appView.appInfo().getMainDexClasses();
- this.mainDexTracingResult = mainDexTracingResult;
- }
-
- @Override
- public MainDexClassification getMergeKey(DexProgramClass clazz) {
- if (mainDexClasses.contains(clazz)) {
- return MainDexClassification.MAIN_DEX_LIST;
- }
- if (mainDexTracingResult.isRoot(clazz)) {
- return MainDexClassification.MAIN_DEX_ROOT;
- }
- if (mainDexTracingResult.isDependency(clazz)) {
- return MainDexClassification.MAIN_DEX_DEPENDENCY;
- }
- return MainDexClassification.NOT_IN_MAIN_DEX;
- }
-}
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/PreventMergeIntoMainDexList.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/PreventMergeIntoMainDexList.java
new file mode 100644
index 0000000..850d02e
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/PreventMergeIntoMainDexList.java
@@ -0,0 +1,25 @@
+// 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.horizontalclassmerging.policies;
+
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.horizontalclassmerging.SingleClassPolicy;
+import com.android.tools.r8.shaking.AppInfoWithLiveness;
+import com.android.tools.r8.shaking.MainDexInfo;
+
+public class PreventMergeIntoMainDexList extends SingleClassPolicy {
+
+ private final MainDexInfo mainDexInfo;
+
+ public PreventMergeIntoMainDexList(AppView<AppInfoWithLiveness> appView) {
+ this.mainDexInfo = appView.appInfo().getMainDexInfo();
+ }
+
+ @Override
+ public boolean canMerge(DexProgramClass program) {
+ return mainDexInfo.canMerge(program);
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/proto/schema/ProtoEnqueuerExtension.java b/src/main/java/com/android/tools/r8/ir/analysis/proto/schema/ProtoEnqueuerExtension.java
index 41ae033..24b17fb 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/proto/schema/ProtoEnqueuerExtension.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/proto/schema/ProtoEnqueuerExtension.java
@@ -381,7 +381,7 @@
// Map/required fields cannot be removed. Therefore, we mark such fields as both read and
// written such that we cannot optimize any field reads or writes.
enqueuer.registerReflectiveFieldAccess(valueStorage.getReference(), dynamicMethod);
- worklist.enqueueMarkReachableFieldAction(
+ worklist.enqueueMarkInstanceFieldAsReachableAction(
valueStorage, dynamicMethod, KeepReason.reflectiveUseIn(dynamicMethod));
valueStorageIsLive = true;
} else {
@@ -446,7 +446,7 @@
// Unconditionally register the hazzer and one-of proto fields as written from
// dynamicMethod().
if (enqueuer.registerReflectiveFieldWrite(newlyLiveField.getReference(), dynamicMethod)) {
- worklist.enqueueMarkReachableFieldAction(
+ worklist.enqueueMarkInstanceFieldAsReachableAction(
newlyLiveField, dynamicMethod, KeepReason.reflectiveUseIn(dynamicMethod));
}
}
@@ -566,7 +566,7 @@
}
if (enqueuer.registerReflectiveFieldWrite(oneOfField.getReference(), dynamicMethod)) {
- worklist.enqueueMarkReachableFieldAction(
+ worklist.enqueueMarkInstanceFieldAsReachableAction(
oneOfField, dynamicMethod, KeepReason.reflectiveUseIn(dynamicMethod));
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/D8MethodProcessor.java b/src/main/java/com/android/tools/r8/ir/conversion/D8MethodProcessor.java
index 61ea47c..d8fb60c 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/D8MethodProcessor.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/D8MethodProcessor.java
@@ -3,6 +3,7 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.ir.conversion;
+import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.graph.ProgramMethod;
import com.android.tools.r8.ir.optimize.info.OptimizationFeedbackIgnore;
import com.android.tools.r8.utils.ThreadUtils;
@@ -24,9 +25,10 @@
this.executorService = executorService;
}
- public void awaitMethodProcessing() throws ExecutionException {
- ThreadUtils.awaitFutures(futures);
- futures.clear();
+ @Override
+ public boolean isProcessedConcurrently(ProgramMethod method) {
+ // In D8 all methods are considered independently compiled.
+ return true;
}
@Override
@@ -43,8 +45,13 @@
executorService));
}
- public boolean verifyAllMethodsProcessed() {
- assert futures.isEmpty();
- return true;
+ @Override
+ public CallSiteInformation getCallSiteInformation() {
+ throw new Unreachable("Invalid attempt to obtain call-site information in D8");
+ }
+
+ public void awaitMethodProcessing() throws ExecutionException {
+ ThreadUtils.awaitFutures(futures);
+ futures.clear();
}
}
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 4131111..b1e68ace 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
@@ -23,7 +23,6 @@
import com.android.tools.r8.graph.DexTypeList;
import com.android.tools.r8.graph.GraphLens;
import com.android.tools.r8.graph.ProgramMethod;
-import com.android.tools.r8.graph.classmerging.HorizontallyMergedLambdaClasses;
import com.android.tools.r8.ir.analysis.TypeChecker;
import com.android.tools.r8.ir.analysis.VerifyTypesHelper;
import com.android.tools.r8.ir.analysis.constant.SparseConditionalConstantPropagation;
@@ -82,7 +81,6 @@
import com.android.tools.r8.ir.optimize.info.OptimizationFeedbackIgnore;
import com.android.tools.r8.ir.optimize.info.OptimizationFeedbackSimple;
import com.android.tools.r8.ir.optimize.info.field.InstanceFieldInitializationInfoCollection;
-import com.android.tools.r8.ir.optimize.lambda.LambdaMerger;
import com.android.tools.r8.ir.optimize.staticizer.ClassStaticizer;
import com.android.tools.r8.ir.optimize.string.StringBuilderOptimizer;
import com.android.tools.r8.ir.optimize.string.StringOptimizer;
@@ -93,7 +91,6 @@
import com.android.tools.r8.position.MethodPosition;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.android.tools.r8.shaking.LibraryMethodOverrideAnalysis;
-import com.android.tools.r8.shaking.MainDexTracingResult;
import com.android.tools.r8.utils.Action;
import com.android.tools.r8.utils.CfgPrinter;
import com.android.tools.r8.utils.DescriptorUtils;
@@ -123,7 +120,6 @@
private static final int PEEPHOLE_OPTIMIZATION_PASSES = 2;
public final AppView<?> appView;
- public final MainDexTracingResult mainDexClasses;
private final Timing timing;
private final Outliner outliner;
@@ -140,7 +136,6 @@
private final TwrCloseResourceRewriter twrCloseResourceRewriter;
private final BackportedMethodRewriter backportedMethodRewriter;
private final DesugaredLibraryRetargeter desugaredLibraryRetargeter;
- private final LambdaMerger lambdaMerger;
private final ClassInliner classInliner;
private final ClassStaticizer classStaticizer;
private final InternalOptions options;
@@ -185,8 +180,7 @@
* 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, CfgPrinter printer, MainDexTracingResult mainDexClasses) {
+ public IRConverter(AppView<?> appView, Timing timing, CfgPrinter printer) {
assert appView.appInfo().hasLiveness() || appView.graphLens().isIdentityLens();
assert appView.options() != null;
assert appView.options().programConsumer != null;
@@ -195,7 +189,6 @@
this.appView = appView;
this.options = appView.options();
this.printer = printer;
- this.mainDexClasses = mainDexClasses;
this.codeRewriter = new CodeRewriter(appView, this);
this.constantCanonicalizer = new ConstantCanonicalizer(codeRewriter);
this.classInitializerDefaultsOptimization =
@@ -242,7 +235,6 @@
: null;
this.d8NestBasedAccessDesugaring =
options.shouldDesugarNests() ? new D8NestBasedAccessDesugaring(appView) : null;
- this.lambdaMerger = null;
this.covariantReturnTypeAnnotationTransformer = null;
this.dynamicTypeOptimization = null;
this.classInliner = null;
@@ -305,12 +297,9 @@
options.enableTreeShakingOfLibraryMethodOverrides
? new LibraryMethodOverrideAnalysis(appViewWithLiveness)
: null;
- this.lambdaMerger =
- options.enableLambdaMerging ? new LambdaMerger(appViewWithLiveness) : null;
this.enumUnboxer = options.enableEnumUnboxing ? new EnumUnboxer(appViewWithLiveness) : null;
this.lensCodeRewriter = new LensCodeRewriter(appViewWithLiveness, enumUnboxer);
- this.inliner =
- new Inliner(appViewWithLiveness, mainDexClasses, lambdaMerger, lensCodeRewriter);
+ this.inliner = new Inliner(appViewWithLiveness, lensCodeRewriter);
this.outliner = new Outliner(appViewWithLiveness);
this.memberValuePropagation =
options.enableValuePropagation ? new MemberValuePropagation(appViewWithLiveness) : null;
@@ -322,9 +311,7 @@
this.identifierNameStringMarker = null;
}
this.devirtualizer =
- options.enableDevirtualization
- ? new Devirtualizer(appViewWithLiveness, mainDexClasses)
- : null;
+ options.enableDevirtualization ? new Devirtualizer(appViewWithLiveness) : null;
this.typeChecker = new TypeChecker(appViewWithLiveness, VerifyTypesHelper.create(appView));
this.d8NestBasedAccessDesugaring = null;
this.serviceLoaderRewriter =
@@ -346,7 +333,6 @@
this.fieldAccessAnalysis = null;
this.libraryMethodOverrideAnalysis = null;
this.inliner = null;
- this.lambdaMerger = null;
this.outliner = null;
this.memberValuePropagation = null;
this.lensCodeRewriter = null;
@@ -372,16 +358,11 @@
/** Create an IR converter for processing methods with full program optimization disabled. */
public IRConverter(AppView<?> appView, Timing timing) {
- this(appView, timing, null, MainDexTracingResult.NONE);
- }
-
- /** Create an IR converter for processing methods with full program optimization disabled. */
- public IRConverter(AppView<?> appView, Timing timing, CfgPrinter printer) {
- this(appView, timing, printer, MainDexTracingResult.NONE);
+ this(appView, timing, null);
}
public IRConverter(AppInfo appInfo, Timing timing, CfgPrinter printer) {
- this(AppView.createForD8(appInfo), timing, printer, MainDexTracingResult.NONE);
+ this(AppView.createForD8(appInfo), timing, printer);
}
private void removeLambdaDeserializationMethods() {
@@ -398,12 +379,8 @@
}
}
- private void finalizeNestBasedAccessDesugaring(Builder<?> builder) {
+ private void reportNestDesugarDependencies() {
if (d8NestBasedAccessDesugaring != null) {
- DexProgramClass nestConstructor = d8NestBasedAccessDesugaring.synthesizeNestConstructor();
- if (nestConstructor != null) {
- builder.addProgramClass(nestConstructor);
- }
d8NestBasedAccessDesugaring.reportDesugarDependencies();
}
}
@@ -489,10 +466,7 @@
convertClasses(application, executor);
- // Build a new application with jumbo string info,
- Builder<?> builder = application.builder().setHighestSortingString(highestSortingString);
-
- finalizeNestBasedAccessDesugaring(builder);
+ reportNestDesugarDependencies();
// Synthesize lambda classes and commit to the app in full.
synthesizeLambdaClasses(executor);
@@ -500,13 +474,14 @@
if (appView.getSyntheticItems().hasPendingSyntheticClasses()) {
appView.setAppInfo(
new AppInfo(
- appView.appInfo().getSyntheticItems().commit(builder.build()),
- appView.appInfo().getMainDexClasses()));
+ appView.appInfo().getSyntheticItems().commit(application),
+ appView.appInfo().getMainDexInfo()));
application = appView.appInfo().app();
- builder = application.builder();
- builder.setHighestSortingString(highestSortingString);
}
+ // Build a new application with jumbo string info,
+ Builder<?> builder = application.builder().setHighestSortingString(highestSortingString);
+
desugarInterfaceMethods(builder, ExcludeDexResources, executor);
processSynthesizedJava8UtilityClasses(executor);
synthesizeRetargetClass(builder, executor);
@@ -520,7 +495,7 @@
appView.setAppInfo(
new AppInfo(
appView.appInfo().getSyntheticItems().commit(application),
- appView.appInfo().getMainDexClasses()));
+ appView.appInfo().getMainDexInfo()));
}
private void convertClasses(DexApplication application, ExecutorService executorService)
@@ -680,7 +655,6 @@
DexApplication application = appView.appInfo().app();
computeReachabilitySensitivity(application);
- collectLambdaMergingCandidates(application);
collectStaticizerCandidates(application);
workaroundAbstractMethodOnNonAbstractClassVerificationBug(
executorService, simpleOptimizationFeedback);
@@ -806,10 +780,6 @@
synthesizeRetargetClass(builder, executorService);
synthesizeEnumUnboxingUtilityMethods(executorService);
- printPhase("Lambda merging finalization");
- // TODO(b/127694949): Adapt to PostOptimization.
- finalizeLambdaMerging(application, feedback, builder, executorService, initialGraphLensForIR);
-
printPhase("Desugared library API Conversion finalization");
generateDesugaredLibraryAPIWrappers(builder, executorService);
@@ -968,28 +938,6 @@
method, code, OptimizationFeedbackIgnore.getInstance(), Timing.empty());
}
- private void collectLambdaMergingCandidates(DexApplication application) {
- if (lambdaMerger != null) {
- lambdaMerger.collectGroupCandidates(application);
- }
- }
-
- private void finalizeLambdaMerging(
- DexApplication application,
- OptimizationFeedback feedback,
- Builder<?> builder,
- ExecutorService executorService,
- GraphLens appliedGraphLens)
- throws ExecutionException {
- if (lambdaMerger != null) {
- lambdaMerger.applyLambdaClassMapping(
- application, this, feedback, builder, executorService, appliedGraphLens);
- } else {
- appView.setHorizontallyMergedLambdaClasses(HorizontallyMergedLambdaClasses.empty());
- }
- assert appView.horizontallyMergedLambdaClasses() != null;
- }
-
private void generateDesugaredLibraryAPIWrappers(
DexApplication.Builder<?> builder, ExecutorService executorService)
throws ExecutionException {
@@ -1239,13 +1187,6 @@
|| !appView.appInfo().withLiveness().isNeverReprocessMethod(method.method)
: "Illegal reprocessing due to -neverreprocess rule: " + context.toSourceString();
- if (lambdaMerger != null) {
- timing.begin("Merge lambdas");
- lambdaMerger.rewriteCode(code.context(), code, inliner, methodProcessor);
- timing.end();
- assert code.isConsistentSSA();
- }
-
if (typeChecker != null && !typeChecker.check(code)) {
assert appView.enableWholeProgramOptimizations();
assert options.testing.allowTypeErrors;
@@ -1335,8 +1276,7 @@
if (appView.appInfo().hasLiveness()) {
// Reflection optimization 1. getClass() / forName() -> const-class
timing.begin("Rewrite to const class");
- ReflectionOptimizer.rewriteGetClassOrForNameToConstClass(
- appView.withLiveness(), code, mainDexClasses);
+ ReflectionOptimizer.rewriteGetClassOrForNameToConstClass(appView.withLiveness(), code);
timing.end();
}
@@ -1548,15 +1488,6 @@
previous = printMethod(code, "IR after twr close resource rewriter (SSA)", previous);
- if (lambdaMerger != null) {
- timing.begin("Analyze lambda merging");
- lambdaMerger.analyzeCode(code.context(), code);
- timing.end();
- assert code.isConsistentSSA();
- }
-
- previous = printMethod(code, "IR after lambda merger (SSA)", previous);
-
// TODO(b/140766440): an ideal solution would be puttting CodeOptimization for this into
// the list for primary processing only.
if (options.outline.enabled && outliner != null && methodProcessor.isPrimaryMethodProcessor()) {
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/MethodProcessor.java b/src/main/java/com/android/tools/r8/ir/conversion/MethodProcessor.java
index ef9a31c..3be22fa 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/MethodProcessor.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/MethodProcessor.java
@@ -1,40 +1,21 @@
-// Copyright (c) 2019, the R8 project authors. Please see the AUTHORS file
+// Copyright (c) 2021, the R8 project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.ir.conversion;
import com.android.tools.r8.graph.ProgramMethod;
-import com.android.tools.r8.utils.collections.SortedProgramMethodSet;
public abstract class MethodProcessor {
- protected SortedProgramMethodSet wave;
- protected SortedProgramMethodSet waveExtension = SortedProgramMethodSet.createConcurrent();
-
- public abstract boolean shouldApplyCodeRewritings(ProgramMethod method);
-
public boolean isPrimaryMethodProcessor() {
return false;
}
- public CallSiteInformation getCallSiteInformation() {
- return CallSiteInformation.empty();
- }
+ public abstract boolean isProcessedConcurrently(ProgramMethod method);
- public boolean isProcessedConcurrently(ProgramMethod method) {
- return wave != null && wave.contains(method);
- }
+ public abstract boolean shouldApplyCodeRewritings(ProgramMethod method);
- public void scheduleMethodForProcessingAfterCurrentWave(ProgramMethod method) {
- waveExtension.add(method);
- }
+ public abstract void scheduleMethodForProcessingAfterCurrentWave(ProgramMethod method);
- protected void prepareForWaveExtensionProcessing() {
- if (waveExtension.isEmpty()) {
- wave = SortedProgramMethodSet.empty();
- } else {
- wave = waveExtension;
- waveExtension = SortedProgramMethodSet.createConcurrent();
- }
- }
+ public abstract CallSiteInformation getCallSiteInformation();
}
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/MethodProcessorWithWave.java b/src/main/java/com/android/tools/r8/ir/conversion/MethodProcessorWithWave.java
new file mode 100644
index 0000000..c50e8e3
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/conversion/MethodProcessorWithWave.java
@@ -0,0 +1,37 @@
+// Copyright (c) 2019, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.ir.conversion;
+
+import com.android.tools.r8.graph.ProgramMethod;
+import com.android.tools.r8.utils.collections.SortedProgramMethodSet;
+
+public abstract class MethodProcessorWithWave extends MethodProcessor {
+
+ protected SortedProgramMethodSet wave;
+ protected SortedProgramMethodSet waveExtension = SortedProgramMethodSet.createConcurrent();
+
+ @Override
+ public CallSiteInformation getCallSiteInformation() {
+ return CallSiteInformation.empty();
+ }
+
+ @Override
+ public boolean isProcessedConcurrently(ProgramMethod method) {
+ return wave != null && wave.contains(method);
+ }
+
+ @Override
+ public void scheduleMethodForProcessingAfterCurrentWave(ProgramMethod method) {
+ waveExtension.add(method);
+ }
+
+ protected void prepareForWaveExtensionProcessing() {
+ if (waveExtension.isEmpty()) {
+ wave = SortedProgramMethodSet.empty();
+ } else {
+ wave = waveExtension;
+ waveExtension = SortedProgramMethodSet.createConcurrent();
+ }
+ }
+}
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 a8c2e57..be427aa 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
@@ -16,7 +16,7 @@
* A {@link MethodProcessor} that doesn't persist; rather just processes the given methods one-time,
* along with a default abstraction of concurrent processing.
*/
-public class OneTimeMethodProcessor extends MethodProcessor {
+public class OneTimeMethodProcessor extends MethodProcessorWithWave {
private final MethodProcessingId.Factory methodProcessingIdFactory;
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/PostMethodProcessor.java b/src/main/java/com/android/tools/r8/ir/conversion/PostMethodProcessor.java
index 649edfd..ba7fa7f 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/PostMethodProcessor.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/PostMethodProcessor.java
@@ -33,7 +33,7 @@
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
-public class PostMethodProcessor extends MethodProcessor {
+public class PostMethodProcessor extends MethodProcessorWithWave {
private final AppView<AppInfoWithLiveness> appView;
private final Collection<CodeOptimization> defaultCodeOptimizations;
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/PrimaryMethodProcessor.java b/src/main/java/com/android/tools/r8/ir/conversion/PrimaryMethodProcessor.java
index cb288b0..065d00b 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/PrimaryMethodProcessor.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/PrimaryMethodProcessor.java
@@ -29,7 +29,7 @@
* A {@link MethodProcessor} that processes methods in the whole program in a bottom-up manner,
* i.e., from leaves to roots.
*/
-class PrimaryMethodProcessor extends MethodProcessor {
+class PrimaryMethodProcessor extends MethodProcessorWithWave {
interface WaveStartAction {
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryAPIConverter.java b/src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryAPIConverter.java
index 860af39..f2f255a 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryAPIConverter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryAPIConverter.java
@@ -64,7 +64,7 @@
public class DesugaredLibraryAPIConverter {
static final String VIVIFIED_PREFIX = "$-vivified-$.";
- private static final String DESCRIPTOR_VIVIFIED_PREFIX = "L$-vivified-$/";
+ public static final String DESCRIPTOR_VIVIFIED_PREFIX = "L$-vivified-$/";
private final AppView<?> appView;
private final DexItemFactory factory;
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryRetargeter.java b/src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryRetargeter.java
index a6606ed..6dbd525 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryRetargeter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryRetargeter.java
@@ -701,7 +701,7 @@
return dispatchTypeFor(method, "dispatchHolder");
}
- private static String getRetargetPackageAndClassPrefixDescriptor(
+ public static String getRetargetPackageAndClassPrefixDescriptor(
DesugaredLibraryConfiguration config) {
return "L"
+ config.getSynthesizedLibraryClassesPackagePrefix()
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/InterfaceMethodRewriter.java b/src/main/java/com/android/tools/r8/ir/desugar/InterfaceMethodRewriter.java
index 1b17172b5..db127f2 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/InterfaceMethodRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/InterfaceMethodRewriter.java
@@ -14,6 +14,7 @@
import static com.android.tools.r8.ir.code.Opcodes.INVOKE_STATIC;
import static com.android.tools.r8.ir.code.Opcodes.INVOKE_SUPER;
import static com.android.tools.r8.ir.code.Opcodes.INVOKE_VIRTUAL;
+import static com.android.tools.r8.ir.desugar.DesugaredLibraryRetargeter.getRetargetPackageAndClassPrefixDescriptor;
import static com.android.tools.r8.ir.desugar.DesugaredLibraryWrapperSynthesizer.TYPE_WRAPPER_SUFFIX;
import static com.android.tools.r8.ir.desugar.DesugaredLibraryWrapperSynthesizer.VIVIFIED_TYPE_WRAPPER_SUFFIX;
@@ -72,7 +73,6 @@
import com.android.tools.r8.origin.Origin;
import com.android.tools.r8.origin.SynthesizedOrigin;
import com.android.tools.r8.position.MethodPosition;
-import com.android.tools.r8.shaking.MainDexClasses;
import com.android.tools.r8.synthesis.SyntheticNaming;
import com.android.tools.r8.synthesis.SyntheticNaming.SyntheticKind;
import com.android.tools.r8.utils.DescriptorUtils;
@@ -97,6 +97,7 @@
import java.util.concurrent.ExecutorService;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
+import java.util.function.Predicate;
//
// Default and static interface method desugaring rewriter (note that lambda
@@ -147,6 +148,7 @@
// Caches default interface method info for already processed interfaces.
private final Map<DexType, DefaultMethodsHelper.Collection> cache = new ConcurrentHashMap<>();
+ private final Predicate<DexType> shouldIgnoreFromReportsPredicate;
/**
* Defines a minor variation in desugaring.
*/
@@ -168,6 +170,7 @@
this.options = appView.options();
this.factory = appView.dexItemFactory();
this.emulatedInterfaces = options.desugaredLibraryConfiguration.getEmulateLibraryInterface();
+ this.shouldIgnoreFromReportsPredicate = getShouldIgnoreFromReportsPredicate(appView);
initializeEmulatedInterfaceVariables();
}
@@ -712,7 +715,6 @@
// Emulated library interfaces should generate the Emulated Library EL dispatch class.
Map<DexType, List<DexType>> emulatedInterfacesHierarchy = processEmulatedInterfaceHierarchy();
AppInfo appInfo = appView.appInfo();
- MainDexClasses mainDexClasses = appInfo.getMainDexClasses();
for (DexType interfaceType : emulatedInterfaces.keySet()) {
DexClass theInterface = appInfo.definitionFor(interfaceType);
if (theInterface == null) {
@@ -724,8 +726,7 @@
theProgramInterface, emulatedInterfacesHierarchy);
if (synthesizedClass != null) {
builder.addSynthesizedClass(synthesizedClass);
- appInfo.addSynthesizedClass(
- synthesizedClass, mainDexClasses.contains(theProgramInterface));
+ appInfo.addSynthesizedClass(synthesizedClass, theProgramInterface);
}
}
}
@@ -954,11 +955,6 @@
return type.descriptor.toString().endsWith(EMULATE_LIBRARY_CLASS_NAME_SUFFIX + ";");
}
- public static boolean isTypeWrapper(DexType type) {
- String name = type.toBinaryName();
- return name.endsWith(TYPE_WRAPPER_SUFFIX) || name.endsWith(VIVIFIED_TYPE_WRAPPER_SUFFIX);
- }
-
// Gets the interface class for a companion class `type`.
private DexType getInterfaceClassType(DexType type) {
return getInterfaceClassType(type, factory);
@@ -1160,7 +1156,6 @@
// make original default methods abstract, remove bridge methods, create dispatch
// classes if needed.
AppInfo appInfo = appView.appInfo();
- MainDexClasses mainDexClasses = appInfo.getMainDexClasses();
InterfaceProcessorNestedGraphLens.Builder graphLensBuilder =
InterfaceProcessorNestedGraphLens.builder();
Map<DexClass, DexProgramClass> classMapping =
@@ -1175,10 +1170,7 @@
// Don't need to optimize synthesized class since all of its methods
// are just moved from interfaces and don't need to be re-processed.
builder.addSynthesizedClass(synthesizedClass);
- boolean addToMainDexClasses =
- interfaceClass.isProgramClass()
- && mainDexClasses.contains(interfaceClass.asProgramClass());
- appInfo.addSynthesizedClass(synthesizedClass, addToMainDexClasses);
+ appInfo.addSynthesizedClass(synthesizedClass, interfaceClass.asProgramClass());
});
new InterfaceMethodRewriterFixup(appView, graphLens).run();
if (appView.options().isDesugaredLibraryCompilation()) {
@@ -1291,13 +1283,34 @@
return true;
}
+ private Predicate<DexType> getShouldIgnoreFromReportsPredicate(AppView<?> appView) {
+ DexItemFactory dexItemFactory = appView.dexItemFactory();
+ InternalOptions options = appView.options();
+ DexString retargetPackageAndClassPrefixDescriptor =
+ dexItemFactory.createString(
+ getRetargetPackageAndClassPrefixDescriptor(options.desugaredLibraryConfiguration));
+ DexString typeWrapperClassNameDescriptorSuffix =
+ dexItemFactory.createString(TYPE_WRAPPER_SUFFIX + ';');
+ DexString vivifiedTypeWrapperClassNameDescriptorSuffix =
+ dexItemFactory.createString(VIVIFIED_TYPE_WRAPPER_SUFFIX + ';');
+ DexString companionClassNameDescriptorSuffix =
+ dexItemFactory.createString(COMPANION_CLASS_NAME_SUFFIX + ";");
+
+ return type -> {
+ DexString descriptor = type.getDescriptor();
+ return appView.rewritePrefix.hasRewrittenType(type, appView)
+ || descriptor.endsWith(typeWrapperClassNameDescriptorSuffix)
+ || descriptor.endsWith(vivifiedTypeWrapperClassNameDescriptorSuffix)
+ || descriptor.endsWith(companionClassNameDescriptorSuffix)
+ || emulatedInterfaces.containsValue(type)
+ || options.desugaredLibraryConfiguration.getCustomConversions().containsValue(type)
+ || appView.getDontWarnConfiguration().matches(type)
+ || descriptor.startsWith(retargetPackageAndClassPrefixDescriptor);
+ };
+ }
+
private boolean shouldIgnoreFromReports(DexType missing) {
- return appView.rewritePrefix.hasRewrittenType(missing, appView)
- || isTypeWrapper(missing)
- || isCompanionClassType(missing)
- || emulatedInterfaces.containsValue(missing)
- || options.desugaredLibraryConfiguration.getCustomConversions().containsValue(missing)
- || appView.getDontWarnConfiguration().matches(missing);
+ return shouldIgnoreFromReportsPredicate.test(missing);
}
void warnMissingInterface(DexClass classToDesugar, DexClass implementing, DexType missing) {
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/LambdaRewriter.java b/src/main/java/com/android/tools/r8/ir/desugar/LambdaRewriter.java
index 165cb52..c8c6a54 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/LambdaRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/LambdaRewriter.java
@@ -60,8 +60,6 @@
*/
public class LambdaRewriter {
- // Public for testing.
- public static final String LAMBDA_GROUP_CLASS_NAME_PREFIX = "-$$LambdaGroup$";
static final String EXPECTED_LAMBDA_METHOD_PREFIX = "lambda$";
public static final String LAMBDA_INSTANCE_FIELD_NAME = "INSTANCE";
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/nest/NestBasedAccessDesugaring.java b/src/main/java/com/android/tools/r8/ir/desugar/nest/NestBasedAccessDesugaring.java
index cbaa05a..c516ce8 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/nest/NestBasedAccessDesugaring.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/nest/NestBasedAccessDesugaring.java
@@ -10,18 +10,14 @@
import com.android.tools.r8.cf.code.CfFieldInstruction;
import com.android.tools.r8.cf.code.CfInstruction;
import com.android.tools.r8.cf.code.CfInvoke;
-import com.android.tools.r8.dex.Constants;
import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.CfCode;
-import com.android.tools.r8.graph.ClassAccessFlags;
import com.android.tools.r8.graph.Code;
-import com.android.tools.r8.graph.DexAnnotationSet;
import com.android.tools.r8.graph.DexClass;
import com.android.tools.r8.graph.DexClassAndField;
import com.android.tools.r8.graph.DexClassAndMember;
import com.android.tools.r8.graph.DexClassAndMethod;
-import com.android.tools.r8.graph.DexEncodedField;
import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.graph.DexMember;
@@ -30,21 +26,20 @@
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.GenericSignature.ClassSignature;
import com.android.tools.r8.graph.LibraryMember;
import com.android.tools.r8.graph.ProgramField;
import com.android.tools.r8.graph.ProgramMethod;
-import com.android.tools.r8.origin.SynthesizedOrigin;
+import com.android.tools.r8.synthesis.SyntheticNaming.SyntheticKind;
import com.android.tools.r8.utils.BooleanUtils;
import com.android.tools.r8.utils.ListUtils;
import com.android.tools.r8.utils.StringDiagnostic;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Iterables;
import com.google.common.collect.Sets;
-import java.util.Collections;
import java.util.List;
+import java.util.Map;
import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Consumer;
import org.objectweb.asm.Opcodes;
@@ -63,14 +58,10 @@
private static final String NEST_ACCESS_FIELD_PUT_NAME_PREFIX = NEST_ACCESS_NAME_PREFIX + "fput";
private static final String NEST_ACCESS_STATIC_PUT_FIELD_NAME_PREFIX =
NEST_ACCESS_NAME_PREFIX + "sfput";
- public static final String NEST_CONSTRUCTOR_NAME = NEST_ACCESS_NAME_PREFIX + "Constructor";
protected final AppView<?> appView;
private final DexItemFactory dexItemFactory;
-
- // Common single empty class for nest based private constructors
- private DexProgramClass nestConstructor;
- private boolean nestConstructorUsed;
+ private final Map<DexType, DexType> syntheticNestConstructorTypes = new ConcurrentHashMap<>();
public NestBasedAccessDesugaring(AppView<?> appView) {
this.appView = appView;
@@ -234,40 +225,6 @@
throw appView.options().errorMissingNestMember(nest);
}
- private DexProgramClass createNestAccessConstructor() {
- // TODO(b/176900254): ensure hygienic synthetic class.
- return new DexProgramClass(
- dexItemFactory.nestConstructorType,
- null,
- new SynthesizedOrigin("Nest based access desugaring", getClass()),
- // Make the synthesized class public since shared in the whole program.
- ClassAccessFlags.fromDexAccessFlags(
- Constants.ACC_FINAL | Constants.ACC_SYNTHETIC | Constants.ACC_PUBLIC),
- dexItemFactory.objectType,
- DexTypeList.empty(),
- dexItemFactory.createString("nest"),
- null,
- Collections.emptyList(),
- null,
- Collections.emptyList(),
- ClassSignature.noSignature(),
- DexAnnotationSet.empty(),
- DexEncodedField.EMPTY_ARRAY,
- DexEncodedField.EMPTY_ARRAY,
- DexEncodedMethod.EMPTY_ARRAY,
- DexEncodedMethod.EMPTY_ARRAY,
- dexItemFactory.getSkipNameValidationForTesting(),
- DexProgramClass::checksumFromType);
- }
-
- public DexProgramClass synthesizeNestConstructor() {
- if (nestConstructorUsed && nestConstructor == null) {
- nestConstructor = createNestAccessConstructor();
- return nestConstructor;
- }
- return null;
- }
-
DexMethod ensureFieldAccessBridge(
DexClassAndField field, boolean isGet, NestBridgeConsumer bridgeConsumer) {
if (field.isProgramField()) {
@@ -360,9 +317,31 @@
private DexMethod getMethodBridgeReference(DexClassAndMethod method) {
if (method.getDefinition().isInstanceInitializer()) {
- DexProto newProto =
- dexItemFactory.appendTypeToProto(method.getProto(), dexItemFactory.nestConstructorType);
- nestConstructorUsed = true;
+ DexType nestConstructorType =
+ syntheticNestConstructorTypes.computeIfAbsent(
+ method.getHolderType(),
+ holder -> {
+ if (method.isProgramMethod()) {
+ return appView
+ .getSyntheticItems()
+ .createFixedClass(
+ SyntheticKind.INIT_TYPE_ARGUMENT,
+ method.asProgramMethod().getHolder(),
+ dexItemFactory,
+ builder -> {})
+ .getType();
+ } else {
+ assert method.isClasspathMethod();
+ return appView
+ .getSyntheticItems()
+ .createFixedClasspathClass(
+ SyntheticKind.INIT_TYPE_ARGUMENT,
+ method.asClasspathMethod().getHolder(),
+ dexItemFactory)
+ .getType();
+ }
+ });
+ DexProto newProto = dexItemFactory.appendTypeToProto(method.getProto(), nestConstructorType);
return method.getReference().withProto(newProto, dexItemFactory);
}
DexProto proto =
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/DefaultInliningOracle.java b/src/main/java/com/android/tools/r8/ir/optimize/DefaultInliningOracle.java
index 79f36ad..969b5d5 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/DefaultInliningOracle.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/DefaultInliningOracle.java
@@ -38,7 +38,6 @@
import com.android.tools.r8.ir.optimize.inliner.WhyAreYouNotInliningReporter;
import com.android.tools.r8.logging.Log;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
-import com.android.tools.r8.shaking.MainDexDirectReferenceTracer;
import com.android.tools.r8.utils.BooleanUtils;
import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.Timing;
@@ -199,25 +198,17 @@
// Don't inline code with references beyond root main dex classes into a root main dex class.
// If we do this it can increase the size of the main dex dependent classes.
if (reason != Reason.FORCE
- && inlineeRefersToClassesNotInMainDex(method.getHolderType(), singleTarget)) {
+ && inliner.mainDexInfo.disallowInliningIntoContext(
+ appView.appInfo(), method, singleTarget)) {
whyAreYouNotInliningReporter.reportInlineeRefersToClassesNotInMainDex();
return false;
}
assert reason != Reason.FORCE
- || !inlineeRefersToClassesNotInMainDex(method.getHolderType(), singleTarget)
- : MainDexDirectReferenceTracer.getFirstReferenceOutsideFromCode(
- appView.appInfo(), singleTarget, inliner.mainDexClasses.getRoots());
+ || !inliner.mainDexInfo.disallowInliningIntoContext(
+ appView.appInfo(), method, singleTarget);
return true;
}
- private boolean inlineeRefersToClassesNotInMainDex(DexType holder, ProgramMethod target) {
- if (inliner.mainDexClasses.isEmpty() || !inliner.mainDexClasses.getRoots().contains(holder)) {
- return false;
- }
- return MainDexDirectReferenceTracer.hasReferencesOutsideFromCode(
- appView.appInfo(), target, inliner.mainDexClasses.getRoots());
- }
-
private boolean satisfiesRequirementsForSimpleInlining(
InvokeMethod invoke, ProgramMethod target) {
// If we are looking for a simple method, only inline if actually simple.
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/Devirtualizer.java b/src/main/java/com/android/tools/r8/ir/optimize/Devirtualizer.java
index 7179327..f5f4d70 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/Devirtualizer.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/Devirtualizer.java
@@ -25,7 +25,6 @@
import com.android.tools.r8.ir.code.InvokeVirtual;
import com.android.tools.r8.ir.code.Value;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
-import com.android.tools.r8.shaking.MainDexTracingResult;
import com.android.tools.r8.utils.InternalOptions;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
@@ -48,13 +47,10 @@
public class Devirtualizer {
private final AppView<AppInfoWithLiveness> appView;
- private final MainDexTracingResult mainDexTracingResult;
private final InternalOptions options;
- public Devirtualizer(
- AppView<AppInfoWithLiveness> appView, MainDexTracingResult mainDexTracingResult) {
+ public Devirtualizer(AppView<AppInfoWithLiveness> appView) {
this.appView = appView;
- this.mainDexTracingResult = mainDexTracingResult;
this.options = appView.options();
}
@@ -154,7 +150,7 @@
DexClassAndMethod reboundTarget = rebindSuperInvokeToMostSpecific(invokedMethod, context);
if (reboundTarget != null
&& reboundTarget.getReference() != invokedMethod
- && !isRebindingNewClassIntoMainDex(invokedMethod, reboundTarget.getReference())) {
+ && !isRebindingNewClassIntoMainDex(context, reboundTarget.getReference())) {
it.replaceCurrentInstruction(
new InvokeSuper(
reboundTarget.getReference(),
@@ -197,7 +193,7 @@
}
// Ensure that we are not adding a new main dex root
- if (isRebindingNewClassIntoMainDex(invoke.getInvokedMethod(), target.getReference())) {
+ if (isRebindingNewClassIntoMainDex(context, target.getReference())) {
continue;
}
@@ -394,13 +390,7 @@
return newResolutionResult.getResolvedMethod().method;
}
- private boolean isRebindingNewClassIntoMainDex(
- DexMethod originalMethod, DexMethod reboundMethod) {
- if (!mainDexTracingResult.isRoot(originalMethod.holder)
- && !appView.appInfo().getMainDexClasses().contains(originalMethod.holder)) {
- return false;
- }
- return !mainDexTracingResult.isRoot(reboundMethod.holder)
- && !appView.appInfo().getMainDexClasses().contains(reboundMethod.holder);
+ private boolean isRebindingNewClassIntoMainDex(ProgramMethod context, DexMethod reboundMethod) {
+ return !appView.appInfo().getMainDexInfo().canRebindReference(context, reboundMethod);
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/Inliner.java b/src/main/java/com/android/tools/r8/ir/optimize/Inliner.java
index 7eca624..e034587 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/Inliner.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/Inliner.java
@@ -57,10 +57,9 @@
import com.android.tools.r8.ir.optimize.inliner.InliningReasonStrategy;
import com.android.tools.r8.ir.optimize.inliner.NopWhyAreYouNotInliningReporter;
import com.android.tools.r8.ir.optimize.inliner.WhyAreYouNotInliningReporter;
-import com.android.tools.r8.ir.optimize.lambda.LambdaMerger;
import com.android.tools.r8.kotlin.Kotlin;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
-import com.android.tools.r8.shaking.MainDexTracingResult;
+import com.android.tools.r8.shaking.MainDexInfo;
import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.IteratorUtils;
import com.android.tools.r8.utils.ListUtils;
@@ -84,9 +83,8 @@
protected final AppView<AppInfoWithLiveness> appView;
private final Set<DexMethod> extraNeverInlineMethods;
- private final LambdaMerger lambdaMerger;
private final LensCodeRewriter lensCodeRewriter;
- final MainDexTracingResult mainDexClasses;
+ final MainDexInfo mainDexInfo;
// State for inlining methods which are known to be called twice.
private boolean applyDoubleInlining = false;
@@ -99,8 +97,6 @@
public Inliner(
AppView<AppInfoWithLiveness> appView,
- MainDexTracingResult mainDexClasses,
- LambdaMerger lambdaMerger,
LensCodeRewriter lensCodeRewriter) {
Kotlin.Intrinsics intrinsics = appView.dexItemFactory().kotlin.intrinsics;
this.appView = appView;
@@ -108,9 +104,8 @@
appView.options().kotlinOptimizationOptions().disableKotlinSpecificOptimizations
? ImmutableSet.of()
: ImmutableSet.of(intrinsics.throwNpe, intrinsics.throwParameterIsNullException);
- this.lambdaMerger = lambdaMerger;
this.lensCodeRewriter = lensCodeRewriter;
- this.mainDexClasses = mainDexClasses;
+ this.mainDexInfo = appView.appInfo().getMainDexInfo();
availableApiExceptions =
appView.options().canHaveDalvikCatchHandlerVerificationBug()
? new AvailableApiExceptions(appView.options())
@@ -596,7 +591,6 @@
InvokeMethod invoke,
ProgramMethod context,
InliningIRProvider inliningIRProvider,
- LambdaMerger lambdaMerger,
LensCodeRewriter lensCodeRewriter) {
DexItemFactory dexItemFactory = appView.dexItemFactory();
InternalOptions options = appView.options();
@@ -743,9 +737,6 @@
assert lensCodeRewriter != null;
lensCodeRewriter.rewrite(code, target);
}
- if (lambdaMerger != null) {
- lambdaMerger.rewriteCodeForInlining(target, code, context, inliningIRProvider);
- }
if (options.testing.inlineeIrModifier != null) {
options.testing.inlineeIrModifier.accept(code);
}
@@ -1040,7 +1031,7 @@
InlineeWithReason inlinee =
action.buildInliningIR(
- appView, invoke, context, inliningIRProvider, lambdaMerger, lensCodeRewriter);
+ appView, invoke, context, inliningIRProvider, lensCodeRewriter);
if (strategy.willExceedBudget(
code, invoke, inlinee, block, whyAreYouNotInliningReporter)) {
assert whyAreYouNotInliningReporter.unsetReasonHasBeenReportedFlag();
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/ReflectionOptimizer.java b/src/main/java/com/android/tools/r8/ir/optimize/ReflectionOptimizer.java
index cc5e7ed..afdf56a 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/ReflectionOptimizer.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/ReflectionOptimizer.java
@@ -26,7 +26,6 @@
import com.android.tools.r8.ir.code.Value;
import com.android.tools.r8.ir.optimize.Inliner.ConstraintWithTarget;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
-import com.android.tools.r8.shaking.MainDexTracingResult;
import com.android.tools.r8.utils.DescriptorUtils;
import com.google.common.collect.Sets;
import java.util.Set;
@@ -37,7 +36,7 @@
// Rewrite getClass() to const-class if the type of the given instance is effectively final.
// Rewrite forName() to const-class if the type is resolvable, accessible and already initialized.
public static void rewriteGetClassOrForNameToConstClass(
- AppView<AppInfoWithLiveness> appView, IRCode code, MainDexTracingResult mainDexClasses) {
+ AppView<AppInfoWithLiveness> appView, IRCode code) {
if (!appView.appInfo().canUseConstClassInstructions(appView.options())) {
return;
}
@@ -62,14 +61,14 @@
context,
invoke.asInvokeStatic(),
rewriteSingleGetClassOrForNameToConstClass(
- appView, code, it, invoke, affectedValues, mainDexClasses));
+ appView, code, it, invoke, affectedValues));
} else {
applyTypeForGetClassTo(
appView,
context,
invoke.asInvokeVirtual(),
rewriteSingleGetClassOrForNameToConstClass(
- appView, code, it, invoke, affectedValues, mainDexClasses));
+ appView, code, it, invoke, affectedValues));
}
}
}
@@ -85,14 +84,15 @@
IRCode code,
InstructionListIterator instructionIterator,
InvokeMethod invoke,
- Set<Value> affectedValues,
- MainDexTracingResult mainDexClasses) {
+ Set<Value> affectedValues) {
return (type, baseClass) -> {
if (invoke.getInvokedMethod().match(appView.dexItemFactory().classMethods.forName)) {
// Bail-out if the optimization could increase the size of the main dex.
if (baseClass.isProgramClass()
- && !mainDexClasses.canReferenceItemFromContextWithoutIncreasingMainDexSize(
- baseClass.asProgramClass(), code.context())) {
+ && !appView
+ .appInfo()
+ .getMainDexInfo()
+ .canRebindReference(code.context(), baseClass.getType())) {
return;
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/InlineCandidateProcessor.java b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/InlineCandidateProcessor.java
index 19a9da1..371b79a 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/InlineCandidateProcessor.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/InlineCandidateProcessor.java
@@ -919,6 +919,12 @@
return false;
}
+ // We cannot guarantee the invoke returns the receiver or another instance and since the
+ // return value is used we have to bail out.
+ if (eligibility.returnsReceiver.isUnknown()) {
+ return false;
+ }
+
// Add the out-value as a definite-alias if the invoke instruction is guaranteed to return the
// receiver. Otherwise, the out-value may be an alias of the receiver, and it is added to the
// may-alias set.
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 bcd8458..17d0cc7 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
@@ -16,6 +16,8 @@
import com.android.tools.r8.graph.DexString;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.DexValue;
+import com.android.tools.r8.ir.analysis.inlining.SimpleInliningConstraint;
+import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.android.tools.r8.utils.BooleanUtils;
import com.android.tools.r8.utils.OptionalBool;
import com.google.common.collect.Sets;
@@ -31,14 +33,14 @@
private final Map<DexType, List<DexEncodedMethod>> unboxedEnumsMethods = new IdentityHashMap<>();
private final EnumUnboxingLens.Builder lensBuilder = EnumUnboxingLens.enumUnboxingLensBuilder();
- private final AppView<?> appView;
+ private final AppView<AppInfoWithLiveness> appView;
private final DexItemFactory factory;
private final Set<DexType> enumsToUnbox;
private final UnboxedEnumMemberRelocator relocator;
private final EnumUnboxingRewriter enumUnboxerRewriter;
EnumUnboxingTreeFixer(
- AppView<?> appView,
+ AppView<AppInfoWithLiveness> appView,
Set<DexType> enumsToUnbox,
UnboxedEnumMemberRelocator relocator,
EnumUnboxingRewriter enumUnboxerRewriter) {
@@ -72,7 +74,9 @@
});
clazz.getMethodCollection().removeMethods(methodsToRemove);
} else {
- clazz.getMethodCollection().replaceMethods(this::fixupEncodedMethod);
+ clazz
+ .getMethodCollection()
+ .replaceMethods(method -> this.fixupEncodedMethod(clazz, method));
fixupFields(clazz.staticFields(), clazz::setStaticField);
fixupFields(clazz.instanceFields(), clazz::setInstanceField);
}
@@ -119,7 +123,7 @@
newMethod, builder -> builder.setCompilationState(encodedMethod.getCompilationState()));
}
- private DexEncodedMethod fixupEncodedMethod(DexEncodedMethod method) {
+ private DexEncodedMethod fixupEncodedMethod(DexProgramClass holder, DexEncodedMethod method) {
DexProto oldProto = method.getProto();
DexProto newProto = fixupProto(oldProto);
if (newProto == method.getProto()) {
@@ -143,21 +147,24 @@
.setCompilationState(method.getCompilationState())
.setIsLibraryMethodOverrideIf(
method.isNonPrivateVirtualMethod(), OptionalBool.FALSE)
- .fixupOptimizationInfo(
- optimizationInfo -> {
- IntList unboxedArgumentIndices = new IntArrayList();
- int offset = BooleanUtils.intValue(method.isInstance());
- for (int i = 0; i < method.getReference().getArity(); i++) {
- if (oldProto.getParameter(i).isReferenceType()
- && newProto.getParameter(i).isPrimitiveType()) {
- unboxedArgumentIndices.add(i + offset);
- }
- }
- optimizationInfo.setSimpleInliningConstraint(
- optimizationInfo
- .getSimpleInliningConstraint()
- .rewrittenWithUnboxedArguments(unboxedArgumentIndices));
- }));
+ .setSimpleInliningConstraint(
+ holder, getRewrittenSimpleInliningConstraint(method, oldProto, newProto)));
+ }
+
+ private SimpleInliningConstraint getRewrittenSimpleInliningConstraint(
+ DexEncodedMethod method, DexProto oldProto, DexProto newProto) {
+ IntList unboxedArgumentIndices = new IntArrayList();
+ int offset = BooleanUtils.intValue(method.isInstance());
+ for (int i = 0; i < method.getReference().getArity(); i++) {
+ if (oldProto.getParameter(i).isReferenceType()
+ && newProto.getParameter(i).isPrimitiveType()) {
+ unboxedArgumentIndices.add(i + offset);
+ }
+ }
+ return method
+ .getOptimizationInfo()
+ .getSimpleInliningConstraint()
+ .rewrittenWithUnboxedArguments(unboxedArgumentIndices);
}
private DexMethod ensureUniqueMethod(DexEncodedMethod encodedMethod, DexMethod newMethod) {
@@ -197,11 +204,8 @@
encodedField.toTypeSubstitutedField(
newField,
builder ->
- builder.fixupOptimizationInfo(
- mutableFieldOptimizationInfo -> {
- mutableFieldOptimizationInfo.setAbstractValue(
- encodedField.getOptimizationInfo().getAbstractValue());
- }));
+ builder.setAbstractValue(
+ encodedField.getOptimizationInfo().getAbstractValue(), appView));
setter.setField(i, newEncodedField);
if (encodedField.isStatic() && encodedField.hasExplicitStaticValue()) {
assert encodedField.getStaticValue() == DexValue.DexValueNull.NULL;
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/enums/UnboxedEnumMemberRelocator.java b/src/main/java/com/android/tools/r8/ir/optimize/enums/UnboxedEnumMemberRelocator.java
index 5a8ae19..48e545c 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/enums/UnboxedEnumMemberRelocator.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/enums/UnboxedEnumMemberRelocator.java
@@ -5,6 +5,7 @@
package com.android.tools.r8.ir.optimize.enums;
import static com.android.tools.r8.ir.optimize.enums.EnumUnboxingRewriter.createValuesField;
+import static com.google.common.base.Predicates.alwaysTrue;
import com.android.tools.r8.dex.Constants;
import com.android.tools.r8.graph.AppView;
@@ -23,6 +24,7 @@
import com.android.tools.r8.graph.ProgramPackageCollection;
import com.android.tools.r8.origin.SynthesizedOrigin;
import com.android.tools.r8.shaking.FieldAccessInfoCollectionModifier;
+import com.android.tools.r8.shaking.MainDexInfo;
import com.android.tools.r8.utils.SetUtils;
import com.google.common.collect.ImmutableMap;
import java.util.ArrayList;
@@ -32,6 +34,7 @@
import java.util.List;
import java.util.Map;
import java.util.Set;
+import java.util.function.Predicate;
public class UnboxedEnumMemberRelocator {
@@ -129,9 +132,8 @@
Set<DexProgramClass> relocatedEnums,
DirectMappedDexApplication.Builder appBuilder,
FieldAccessInfoCollectionModifier.Builder fieldAccessInfoCollectionModifierBuilder) {
- DexType deterministicContextType = findDeterministicContextType(contexts);
- assert deterministicContextType.isClassType();
- String descriptorString = deterministicContextType.toDescriptorString();
+ DexProgramClass deterministicContext = findDeterministicContextType(contexts, alwaysTrue());
+ String descriptorString = deterministicContext.getType().toDescriptorString();
String descriptorPrefix = descriptorString.substring(0, descriptorString.length() - 1);
String syntheticClassDescriptor = descriptorPrefix + ENUM_UNBOXING_UTILITY_CLASS_SUFFIX + ";";
DexType type = appView.dexItemFactory().createType(syntheticClassDescriptor);
@@ -180,20 +182,25 @@
appView.dexItemFactory().getSkipNameValidationForTesting(),
DexProgramClass::checksumFromType);
appBuilder.addSynthesizedClass(syntheticClass);
+ MainDexInfo mainDexInfo = appView.appInfo().getMainDexInfo();
appView
.appInfo()
.addSynthesizedClass(
- syntheticClass, appView.appInfo().getMainDexClasses().containsAnyOf(contexts));
+ syntheticClass, findDeterministicContextType(contexts, mainDexInfo::isMainDex));
return syntheticClass;
}
- private DexType findDeterministicContextType(Set<DexProgramClass> contexts) {
- DexType deterministicContext = null;
+ private DexProgramClass findDeterministicContextType(
+ Set<DexProgramClass> contexts, Predicate<DexProgramClass> predicate) {
+ DexProgramClass deterministicContext = null;
for (DexProgramClass context : contexts) {
+ if (!predicate.test(context)) {
+ continue;
+ }
if (deterministicContext == null) {
- deterministicContext = context.type;
- } else if (context.type.compareTo(deterministicContext) < 0) {
- deterministicContext = context.type;
+ deterministicContext = context;
+ } else if (context.type.compareTo(deterministicContext.type) < 0) {
+ deterministicContext = context;
}
}
return deterministicContext;
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/MutableFieldOptimizationInfo.java b/src/main/java/com/android/tools/r8/ir/optimize/info/MutableFieldOptimizationInfo.java
index 3bf8d88..41ee658 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/info/MutableFieldOptimizationInfo.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/MutableFieldOptimizationInfo.java
@@ -65,7 +65,7 @@
return abstractValue;
}
- public void setAbstractValue(AbstractValue abstractValue) {
+ void setAbstractValue(AbstractValue abstractValue) {
this.abstractValue = abstractValue;
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/OptimizationFeedbackSimple.java b/src/main/java/com/android/tools/r8/ir/optimize/info/OptimizationFeedbackSimple.java
index e62b2c1..945d84a 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/info/OptimizationFeedbackSimple.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/OptimizationFeedbackSimple.java
@@ -66,7 +66,9 @@
@Override
public void recordFieldHasAbstractValue(
DexEncodedField field, AppView<AppInfoWithLiveness> appView, AbstractValue abstractValue) {
- // Ignored.
+ if (appView.appInfo().mayPropagateValueFor(field.field)) {
+ field.getMutableOptimizationInfo().setAbstractValue(abstractValue);
+ }
}
// METHOD OPTIMIZATION INFO.
@@ -198,7 +200,7 @@
@Override
public void setSimpleInliningConstraint(
ProgramMethod method, SimpleInliningConstraint constraint) {
- // Ignored.
+ method.getDefinition().getMutableOptimizationInfo().setSimpleInliningConstraint(constraint);
}
@Override
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/UpdatableMethodOptimizationInfo.java b/src/main/java/com/android/tools/r8/ir/optimize/info/UpdatableMethodOptimizationInfo.java
index 03e83b8..a179561 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/info/UpdatableMethodOptimizationInfo.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/UpdatableMethodOptimizationInfo.java
@@ -383,7 +383,7 @@
setFlag(REACHABILITY_SENSITIVE_FLAG, reachabilitySensitive);
}
- public void setSimpleInliningConstraint(SimpleInliningConstraint constraint) {
+ void setSimpleInliningConstraint(SimpleInliningConstraint constraint) {
this.simpleInliningConstraint = constraint;
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/lambda/CaptureSignature.java b/src/main/java/com/android/tools/r8/ir/optimize/lambda/CaptureSignature.java
deleted file mode 100644
index 5a39e71..0000000
--- a/src/main/java/com/android/tools/r8/ir/optimize/lambda/CaptureSignature.java
+++ /dev/null
@@ -1,160 +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.ir.optimize.lambda;
-
-import com.android.tools.r8.errors.Unreachable;
-import com.android.tools.r8.graph.DexEncodedField;
-import com.android.tools.r8.graph.DexField;
-import com.android.tools.r8.graph.DexItemFactory;
-import com.android.tools.r8.graph.DexType;
-import com.android.tools.r8.graph.DexTypeList;
-import it.unimi.dsi.fastutil.ints.IntArrayList;
-import it.unimi.dsi.fastutil.ints.IntList;
-import it.unimi.dsi.fastutil.ints.IntLists;
-import java.util.Arrays;
-import java.util.BitSet;
-import java.util.Comparator;
-import java.util.List;
-import java.util.function.IntFunction;
-
-// While mapping fields representing lambda captures we rearrange fields to make sure
-// lambdas having different order of the fields still can be merged. We also store
-// all captures of reference types in fields of java.lang.Objects class.
-//
-// This allows us to use same lambda groups class for these two different lambdas:
-//
-// Lambda Group Class Lambda$1 Lambda$2
-// Object $c0 int foo -> $c1 char foo -> $c2
-// int $c1 String[] bar -> $c0 int bar -> $c1
-// char $c2 char baz -> $c2 List baz -> $c0
-//
-// Capture signature is represented by a string with sorted shorties of field
-// types, such as "CIL" for both Lambda$1 and Lambda$2 in the above example.
-//
-public final class CaptureSignature {
- private static final IntList EMPTY_LIST = IntLists.EMPTY_LIST;
- private static final IntList SINGLE_LIST = IntLists.singleton(0);
-
- private CaptureSignature() {
- }
-
- // Returns an array representing mapping of fields of the normalized capture
- // into fields of original capture such that:
- //
- // mapping = getReverseCaptureMapping(...)
- // <original-capture-index> = mapping[<normalized-capture-index>]
- public static IntList getReverseCaptureMapping(DexType[] types) {
- if (types.length == 0) {
- return EMPTY_LIST;
- }
-
- if (types.length == 1) {
- return SINGLE_LIST;
- }
-
- IntList result = new IntArrayList(types.length);
- for (int i = 0; i < types.length; i++) {
- result.add(i);
- }
- // Sort the indices by shorties (sorting is stable).
- result.sort(Comparator.comparingInt(i -> types[i].toShorty()));
- assert verifyMapping(result);
- return result;
- }
-
- // Given a capture signature and an index returns the type of the field.
- public static DexType fieldType(DexItemFactory factory, String capture, int index) {
- switch (capture.charAt(index)) {
- case 'L':
- return factory.objectType;
- case 'Z':
- return factory.booleanType;
- case 'B':
- return factory.byteType;
- case 'S':
- return factory.shortType;
- case 'C':
- return factory.charType;
- case 'I':
- return factory.intType;
- case 'F':
- return factory.floatType;
- case 'J':
- return factory.longType;
- case 'D':
- return factory.doubleType;
- default:
- throw new Unreachable("Invalid capture character: " + capture.charAt(index));
- }
- }
-
- private static String getCaptureSignature(int size, IntFunction<DexType> type) {
- if (size == 0) {
- return "";
- }
- if (size == 1) {
- return Character.toString(type.apply(0).toShorty());
- }
-
- char[] chars = new char[size];
- for (int i = 0; i < size; i++) {
- chars[i] = type.apply(i).toShorty();
- }
- Arrays.sort(chars);
- return new String(chars);
- }
-
- // Compute capture signature based on lambda class capture fields.
- public static String getCaptureSignature(List<DexEncodedField> fields) {
- return getCaptureSignature(fields.size(), i -> fields.get(i).field.type);
- }
-
- // Compute capture signature based on type list.
- public static String getCaptureSignature(DexTypeList types) {
- return getCaptureSignature(types.values.length, i -> types.values[i]);
- }
-
- // Having a list of fields of lambda captured values, maps one of them into
- // an index of the appropriate field in normalized capture signature.
- public static int mapFieldIntoCaptureIndex(
- String capture, List<DexEncodedField> lambdaFields, DexField fieldToMap) {
- char fieldKind = fieldToMap.type.toShorty();
- int numberOfSameCaptureKind = 0;
- int result = -1;
-
- for (DexEncodedField encodedField : lambdaFields) {
- if (encodedField.field == fieldToMap) {
- result = numberOfSameCaptureKind;
- break;
- }
- if (encodedField.field.type.toShorty() == fieldKind) {
- numberOfSameCaptureKind++;
- }
- }
-
- assert result >= 0 : "Field was not found in the lambda class.";
-
- for (int index = 0; index < capture.length(); index++) {
- if (capture.charAt(index) == fieldKind) {
- if (result == 0) {
- return index;
- }
- result--;
- }
- }
-
- throw new Unreachable("Were not able to map lambda field into capture index");
- }
-
- private static boolean verifyMapping(IntList mapping) {
- BitSet bits = new BitSet();
- for (Integer i : mapping) {
- assert i >= 0 && i < mapping.size();
- assert !bits.get(i);
- bits.set(i);
- }
- return true;
- }
-}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/lambda/CodeProcessor.java b/src/main/java/com/android/tools/r8/ir/optimize/lambda/CodeProcessor.java
deleted file mode 100644
index 1da512d..0000000
--- a/src/main/java/com/android/tools/r8/ir/optimize/lambda/CodeProcessor.java
+++ /dev/null
@@ -1,430 +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.ir.optimize.lambda;
-
-import com.android.tools.r8.errors.Unreachable;
-import com.android.tools.r8.graph.AppView;
-import com.android.tools.r8.graph.DexField;
-import com.android.tools.r8.graph.DexItemFactory;
-import com.android.tools.r8.graph.DexMethod;
-import com.android.tools.r8.graph.DexType;
-import com.android.tools.r8.graph.ProgramMethod;
-import com.android.tools.r8.ir.code.Argument;
-import com.android.tools.r8.ir.code.BasicBlock;
-import com.android.tools.r8.ir.code.CheckCast;
-import com.android.tools.r8.ir.code.ConstClass;
-import com.android.tools.r8.ir.code.ConstMethodHandle;
-import com.android.tools.r8.ir.code.ConstMethodType;
-import com.android.tools.r8.ir.code.DefaultInstructionVisitor;
-import com.android.tools.r8.ir.code.IRCode;
-import com.android.tools.r8.ir.code.InitClass;
-import com.android.tools.r8.ir.code.InstanceGet;
-import com.android.tools.r8.ir.code.InstancePut;
-import com.android.tools.r8.ir.code.InstructionListIterator;
-import com.android.tools.r8.ir.code.Invoke;
-import com.android.tools.r8.ir.code.InvokeMethod;
-import com.android.tools.r8.ir.code.NewArrayEmpty;
-import com.android.tools.r8.ir.code.NewInstance;
-import com.android.tools.r8.ir.code.StaticGet;
-import com.android.tools.r8.ir.code.StaticPut;
-import com.android.tools.r8.ir.optimize.lambda.LambdaMerger.ApplyStrategy;
-import com.android.tools.r8.kotlin.Kotlin;
-import com.android.tools.r8.shaking.AppInfoWithLiveness;
-import java.util.ListIterator;
-import java.util.function.Function;
-
-// Performs processing of the method code (all methods) needed by lambda rewriter.
-//
-// Functionality can be modified by strategy (specific to lambda group) and lambda
-// type visitor.
-//
-// In general it is used to
-// (a) track the code for illegal lambda type usages inside the code, and
-// (b) patching valid lambda type references to point to lambda group classes instead.
-//
-// This class is also used inside particular strategies as a context of the instruction
-// being checked or patched, it provides access to code, block and instruction iterators.
-public abstract class CodeProcessor extends DefaultInstructionVisitor<Void> {
- // Strategy (specific to lambda group) for detecting valid references to the
- // lambda classes (of this group) and patching them with group class references.
- public interface Strategy {
- LambdaGroup group();
-
- boolean isValidStaticFieldWrite(CodeProcessor context, DexField field);
-
- boolean isValidStaticFieldRead(CodeProcessor context, DexField field);
-
- boolean isValidInstanceFieldWrite(CodeProcessor context, DexField field);
-
- boolean isValidInstanceFieldRead(CodeProcessor context, DexField field);
-
- boolean isValidInvoke(CodeProcessor context, InvokeMethod invoke);
-
- boolean isValidNewInstance(CodeProcessor context, NewInstance invoke);
-
- boolean isValidInitClass(CodeProcessor context, DexType clazz);
-
- boolean isValidHolder(CodeProcessor context, DexType holder);
-
- void patch(ApplyStrategy context, NewInstance newInstance);
-
- void patch(ApplyStrategy context, InvokeMethod invoke);
-
- void patch(ApplyStrategy context, InstanceGet instanceGet);
-
- void patch(ApplyStrategy context, StaticGet staticGet);
-
- void patch(ApplyStrategy context, InitClass initClass);
-
- void patch(ApplyStrategy context, Argument argument);
- }
-
- // No-op strategy.
- static final Strategy NoOp =
- new Strategy() {
- @Override
- public LambdaGroup group() {
- return null;
- }
-
- @Override
- public boolean isValidInstanceFieldWrite(CodeProcessor context, DexField field) {
- return false;
- }
-
- @Override
- public boolean isValidInstanceFieldRead(CodeProcessor context, DexField field) {
- return false;
- }
-
- @Override
- public boolean isValidStaticFieldWrite(CodeProcessor context, DexField field) {
- return false;
- }
-
- @Override
- public boolean isValidStaticFieldRead(CodeProcessor context, DexField field) {
- return false;
- }
-
- @Override
- public boolean isValidInvoke(CodeProcessor context, InvokeMethod invoke) {
- return false;
- }
-
- @Override
- public boolean isValidNewInstance(CodeProcessor context, NewInstance invoke) {
- return false;
- }
-
- @Override
- public boolean isValidInitClass(CodeProcessor context, DexType clazz) {
- return false;
- }
-
- @Override
- public boolean isValidHolder(CodeProcessor context, DexType holder) {
- return false;
- }
-
- @Override
- public void patch(ApplyStrategy context, NewInstance newInstance) {
- throw new Unreachable();
- }
-
- @Override
- public void patch(ApplyStrategy context, InvokeMethod invoke) {
- throw new Unreachable();
- }
-
- @Override
- public void patch(ApplyStrategy context, InstanceGet instanceGet) {
- throw new Unreachable();
- }
-
- @Override
- public void patch(ApplyStrategy context, StaticGet staticGet) {
- throw new Unreachable();
- }
-
- @Override
- public void patch(ApplyStrategy context, InitClass initClass) {
- throw new Unreachable();
- }
-
- @Override
- public void patch(ApplyStrategy context, Argument argument) {
- throw new Unreachable();
- }
- };
-
- public final AppView<AppInfoWithLiveness> appView;
- public final DexItemFactory factory;
- public final Kotlin kotlin;
-
- // Defines a factory providing a strategy for a lambda type, returns
- // NoOp strategy if the type is not a lambda.
- private final Function<DexType, Strategy> strategyProvider;
-
- // Visitor for lambda type references seen in unexpected places. Either
- // invalidates the lambda or asserts depending on the processing phase.
- private final LambdaTypeVisitor lambdaChecker;
-
- // Specify the context of the current instruction: method/code/blocks/instructions.
- public final ProgramMethod method;
- public final IRCode code;
- public final ListIterator<BasicBlock> blocks;
- private InstructionListIterator instructions;
-
- // The inlining context (caller), if any.
- private final ProgramMethod context;
-
- CodeProcessor(
- AppView<AppInfoWithLiveness> appView,
- Function<DexType, Strategy> strategyProvider,
- LambdaTypeVisitor lambdaChecker,
- ProgramMethod method,
- IRCode code) {
- this(appView, strategyProvider, lambdaChecker, method, code, null);
- }
-
- CodeProcessor(
- AppView<AppInfoWithLiveness> appView,
- Function<DexType, Strategy> strategyProvider,
- LambdaTypeVisitor lambdaChecker,
- ProgramMethod method,
- IRCode code,
- ProgramMethod context) {
- this.appView = appView;
- this.strategyProvider = strategyProvider;
- this.factory = appView.dexItemFactory();
- this.kotlin = factory.kotlin;
- this.lambdaChecker = lambdaChecker;
- this.method = method;
- this.code = code;
- this.blocks = code.listIterator();
- this.context = context;
- }
-
- public final InstructionListIterator instructions() {
- assert instructions != null;
- return instructions;
- }
-
- void processCode() {
- while (blocks.hasNext()) {
- BasicBlock block = blocks.next();
- instructions = block.listIterator(code);
- while (instructions.hasNext()) {
- instructions.next().accept(this);
- }
- }
- }
-
- private boolean shouldRewrite(DexField field) {
- return shouldRewrite(field.holder);
- }
-
- private boolean shouldRewrite(DexMethod method) {
- return shouldRewrite(method.holder);
- }
-
- private boolean shouldRewrite(DexType type) {
- // Rewrite references to lambda classes if we are outside the class.
- return type != (context != null ? context : method).getHolderType();
- }
-
- @Override
- public Void handleInvoke(Invoke invoke) {
- if (invoke.isInvokeNewArray()) {
- lambdaChecker.accept(invoke.asInvokeNewArray().getReturnType());
- return null;
- }
- if (invoke.isInvokeMultiNewArray()) {
- lambdaChecker.accept(invoke.asInvokeMultiNewArray().getReturnType());
- return null;
- }
- if (invoke.isInvokeCustom()) {
- lambdaChecker.accept(invoke.asInvokeCustom().getCallSite());
- return null;
- }
-
- InvokeMethod invokeMethod = invoke.asInvokeMethod();
- Strategy strategy = strategyProvider.apply(invokeMethod.getInvokedMethod().holder);
- if (strategy.isValidInvoke(this, invokeMethod)) {
- // Invalidate signature, there still should not be lambda references.
- lambdaChecker.accept(invokeMethod.getInvokedMethod().proto);
- // Only rewrite references to lambda classes if we are outside the class.
- if (shouldRewrite(invokeMethod.getInvokedMethod())) {
- process(strategy, invokeMethod);
- }
- return null;
- }
-
- // For the rest invalidate any references.
- if (invoke.isInvokePolymorphic()) {
- lambdaChecker.accept(invoke.asInvokePolymorphic().getProto());
- }
- lambdaChecker.accept(invokeMethod.getInvokedMethod(), null);
- return null;
- }
-
- @Override
- public Void visit(NewInstance newInstance) {
- Strategy strategy = strategyProvider.apply(newInstance.clazz);
- if (strategy.isValidNewInstance(this, newInstance)) {
- // Only rewrite references to lambda classes if we are outside the class.
- if (shouldRewrite(newInstance.clazz)) {
- process(strategy, newInstance);
- }
- }
- return null;
- }
-
- @Override
- public Void visit(CheckCast checkCast) {
- lambdaChecker.accept(checkCast.getType());
- return null;
- }
-
- @Override
- public Void visit(NewArrayEmpty newArrayEmpty) {
- lambdaChecker.accept(newArrayEmpty.type);
- return null;
- }
-
- @Override
- public Void visit(ConstClass constClass) {
- lambdaChecker.accept(constClass.getValue());
- return null;
- }
-
- @Override
- public Void visit(ConstMethodType constMethodType) {
- lambdaChecker.accept(constMethodType.getValue());
- return null;
- }
-
- @Override
- public Void visit(ConstMethodHandle constMethodHandle) {
- lambdaChecker.accept(constMethodHandle.getValue());
- return null;
- }
-
- @Override
- public Void visit(InstanceGet instanceGet) {
- DexField field = instanceGet.getField();
- Strategy strategy = strategyProvider.apply(field.holder);
- if (strategy.isValidInstanceFieldRead(this, field)) {
- if (shouldRewrite(field)) {
- // Only rewrite references to lambda classes if we are outside the class.
- process(strategy, instanceGet);
- }
- } else {
- lambdaChecker.accept(field.type);
- }
-
- // We avoid fields with type being lambda class, it is possible for
- // a lambda to capture another lambda, but we don't support it for now.
- lambdaChecker.accept(field.type);
- return null;
- }
-
- @Override
- public Void visit(InstancePut instancePut) {
- DexField field = instancePut.getField();
- Strategy strategy = strategyProvider.apply(field.holder);
- if (strategy.isValidInstanceFieldWrite(this, field)) {
- if (shouldRewrite(field)) {
- // Only rewrite references to lambda classes if we are outside the class.
- process(strategy, instancePut);
- }
- } else {
- lambdaChecker.accept(field.type);
- }
-
- // We avoid fields with type being lambda class, it is possible for
- // a lambda to capture another lambda, but we don't support it for now.
- lambdaChecker.accept(field.type);
- return null;
- }
-
- @Override
- public Void visit(StaticGet staticGet) {
- DexField field = staticGet.getField();
- Strategy strategy = strategyProvider.apply(field.holder);
- if (strategy.isValidStaticFieldRead(this, field)) {
- if (shouldRewrite(field)) {
- // Only rewrite references to lambda classes if we are outside the class.
- process(strategy, staticGet);
- }
- } else {
- lambdaChecker.accept(field.type);
- lambdaChecker.accept(field.holder);
- }
- return null;
- }
-
- @Override
- public Void visit(StaticPut staticPut) {
- DexField field = staticPut.getField();
- Strategy strategy = strategyProvider.apply(field.holder);
- if (strategy.isValidStaticFieldWrite(this, field)) {
- if (shouldRewrite(field)) {
- // Only rewrite references to lambda classes if we are outside the class.
- process(strategy, staticPut);
- }
- } else {
- lambdaChecker.accept(field.type);
- lambdaChecker.accept(field.holder);
- }
- return null;
- }
-
- @Override
- public Void visit(InitClass initClass) {
- DexType clazz = initClass.getClassValue();
- Strategy strategy = strategyProvider.apply(clazz);
- if (strategy.isValidInitClass(this, clazz)) {
- if (shouldRewrite(clazz)) {
- // Only rewrite references to lambda classes if we are outside the class.
- process(strategy, initClass);
- }
- } else {
- lambdaChecker.accept(clazz);
- }
- return null;
- }
-
- @Override
- public Void visit(Argument instruction) {
- if (instruction.outValue() != code.getThis()) {
- return null;
- }
- Strategy strategy = strategyProvider.apply(method.getHolderType());
- if (strategy.isValidHolder(this, method.getHolderType())) {
- if (shouldRewrite(method.getHolderType())) {
- process(strategy, instruction);
- }
- }
- return null;
- }
-
- abstract void process(Strategy strategy, InvokeMethod invokeMethod);
-
- abstract void process(Strategy strategy, NewInstance newInstance);
-
- abstract void process(Strategy strategy, InstancePut instancePut);
-
- abstract void process(Strategy strategy, InstanceGet instanceGet);
-
- abstract void process(Strategy strategy, StaticPut staticPut);
-
- abstract void process(Strategy strategy, StaticGet staticGet);
-
- abstract void process(Strategy strategy, InitClass initClass);
-
- abstract void process(Strategy strategy, Argument argument);
-}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/lambda/LambdaGroup.java b/src/main/java/com/android/tools/r8/ir/optimize/lambda/LambdaGroup.java
deleted file mode 100644
index cc01f83..0000000
--- a/src/main/java/com/android/tools/r8/ir/optimize/lambda/LambdaGroup.java
+++ /dev/null
@@ -1,237 +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.ir.optimize.lambda;
-
-import com.android.tools.r8.errors.Unreachable;
-import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
-import com.android.tools.r8.graph.AppView;
-import com.android.tools.r8.graph.DexClass;
-import com.android.tools.r8.graph.DexEncodedField;
-import com.android.tools.r8.graph.DexItemFactory;
-import com.android.tools.r8.graph.DexProgramClass;
-import com.android.tools.r8.graph.DexString;
-import com.android.tools.r8.graph.DexType;
-import com.android.tools.r8.ir.optimize.info.OptimizationFeedback;
-import com.android.tools.r8.ir.optimize.lambda.CodeProcessor.Strategy;
-import com.android.tools.r8.kotlin.Kotlin;
-import com.android.tools.r8.shaking.MainDexClasses;
-import com.android.tools.r8.utils.InternalOptions;
-import com.android.tools.r8.utils.ThrowingConsumer;
-import com.google.common.collect.Lists;
-import com.google.common.io.BaseEncoding;
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-import java.io.ObjectOutputStream;
-import java.security.MessageDigest;
-import java.security.NoSuchAlgorithmException;
-import java.util.LinkedHashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.Map.Entry;
-import java.util.function.Consumer;
-import java.util.function.Predicate;
-
-// Represents a group of lambda classes which potentially can be represented
-// by the same lambda _group_ class. Each lambda class inside the group is
-// assigned an integer id.
-//
-// NOTE: access to lambdas in lambda group is NOT thread-safe.
-public abstract class LambdaGroup {
- public final LambdaGroupId id;
-
- // Lambda group class name. Is intended to be stable and uniques.
- // In current implementation is generated in following way:
- // <optional-package>.-$$LambdaGroup$<HASH>
- // with HASH generated based on names of the lambda class names
- // of lambdas included in the group.
- private DexType classType;
-
- // Maps lambda classes belonging to the group into the index inside the
- // group. Note usage of linked hash map to keep insertion ordering stable.
- private final Map<DexType, LambdaInfo> lambdas = new LinkedHashMap<>();
-
- public static class LambdaInfo {
- public int id;
- public final DexProgramClass clazz;
-
- LambdaInfo(int id, DexProgramClass clazz) {
- this.id = id;
- this.clazz = clazz;
- }
-
- public DexProgramClass getLambdaClass() {
- return clazz;
- }
- }
-
- public LambdaGroup(LambdaGroupId id) {
- this.id = id;
- }
-
- public final DexType getGroupClassType() {
- assert classType != null;
- return classType;
- }
-
- public final int size() {
- return lambdas.size();
- }
-
- public final void forEachLambda(Consumer<LambdaInfo> action) {
- assert verifyLambdaIds(false);
- for (LambdaInfo info : lambdas.values()) {
- action.accept(info);
- }
- }
-
- public final boolean anyLambda(Predicate<LambdaInfo> predicate) {
- assert verifyLambdaIds(false);
- for (LambdaInfo info : lambdas.values()) {
- if (predicate.test(info)) {
- return true;
- }
- }
- return false;
- }
-
- final boolean shouldAddToMainDex(AppView<?> appView) {
- // We add the group class to main index if any of the
- // lambda classes it replaces is added to main index.
- MainDexClasses mainDexClasses = appView.appInfo().getMainDexClasses();
- for (LambdaInfo info : lambdas.values()) {
- if (mainDexClasses.contains(info.getLambdaClass())) {
- return true;
- }
- }
- return false;
- }
-
- public final boolean containsLambda(DexType lambda) {
- return lambdas.containsKey(lambda);
- }
-
- public final int lambdaId(DexType lambda) {
- assert lambdas.containsKey(lambda);
- return lambdas.get(lambda).id;
- }
-
- protected final List<DexEncodedField> lambdaCaptureFields(DexType lambda) {
- assert lambdas.containsKey(lambda);
- return lambdas.get(lambda).clazz.instanceFields();
- }
-
- protected final DexEncodedField lambdaSingletonField(DexType lambda) {
- assert lambdas.containsKey(lambda);
- List<DexEncodedField> fields = lambdas.get(lambda).clazz.staticFields();
- assert fields.size() < 2;
- return fields.size() == 0 ? null : fields.get(0);
- }
-
- // Contains less than 2 elements?
- final boolean isTrivial() {
- return lambdas.size() < 2;
- }
-
- final void add(DexProgramClass lambda) {
- assert !lambdas.containsKey(lambda.type);
- lambdas.put(lambda.type, new LambdaInfo(lambdas.size(), lambda));
- }
-
- final void remove(DexType lambda) {
- assert lambdas.containsKey(lambda);
- lambdas.remove(lambda);
- }
-
- final void compact() {
- assert verifyLambdaIds(false);
- int lastUsed = -1;
- int lastSeen = -1;
- for (Entry<DexType, LambdaInfo> entry : lambdas.entrySet()) {
- int index = entry.getValue().id;
- assert lastUsed <= lastSeen && lastSeen < index;
- lastUsed++;
- lastSeen = index;
- if (lastUsed < index) {
- entry.getValue().id = lastUsed;
- }
- }
- assert verifyLambdaIds(true);
- }
-
- public abstract Strategy getCodeStrategy();
-
- public abstract ThrowingConsumer<DexClass, LambdaStructureError> lambdaClassValidator(
- Kotlin kotlin, AppInfoWithClassHierarchy appInfo);
-
- // Package for a lambda group class to be created in.
- protected abstract String getTypePackage();
-
- protected abstract String getGroupSuffix();
-
- final DexProgramClass synthesizeClass(
- AppView<? extends AppInfoWithClassHierarchy> appView, OptimizationFeedback feedback) {
- assert classType == null;
- assert verifyLambdaIds(true);
- List<LambdaInfo> lambdas = Lists.newArrayList(this.lambdas.values());
- classType =
- appView
- .dexItemFactory()
- .createType(
- "L"
- + getTypePackage()
- + "-$$LambdaGroup$"
- + getGroupSuffix()
- + createHash(lambdas)
- + ";");
- return getBuilder(appView.dexItemFactory(), appView.options())
- .synthesizeClass(appView, feedback);
- }
-
- protected abstract LambdaGroupClassBuilder<? extends LambdaGroup> getBuilder(
- DexItemFactory factory, InternalOptions options);
-
- private String createHash(List<LambdaInfo> lambdas) {
- try {
- ByteArrayOutputStream bytes = new ByteArrayOutputStream();
- ObjectOutputStream out = new ObjectOutputStream(bytes);
-
- // We will generate SHA-1 hash of the list of lambda classes represented in the group.
- for (LambdaInfo lambda : lambdas) {
- DexString descriptor = lambda.clazz.type.descriptor;
- out.writeInt(descriptor.size); // To avoid same-prefix problem
- out.write(descriptor.content);
- }
- out.close();
-
- MessageDigest digest = MessageDigest.getInstance("SHA-1");
- digest.update(bytes.toByteArray());
- return BaseEncoding.base64Url().omitPadding().encode(digest.digest());
- } catch (NoSuchAlgorithmException | IOException ex) {
- throw new Unreachable("Cannot get SHA-1 message digest");
- }
- }
-
- private boolean verifyLambdaIds(boolean strict) {
- int previous = -1;
- for (LambdaInfo info : lambdas.values()) {
- assert strict ? (previous + 1) == info.id : previous < info.id;
- previous = info.id;
- }
- return true;
- }
-
- public static class LambdaStructureError extends Exception {
- final boolean reportable;
-
- public LambdaStructureError(String cause) {
- this(cause, true);
- }
-
- public LambdaStructureError(String cause, boolean reportable) {
- super(cause);
- this.reportable = reportable;
- }
- }
-}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/lambda/LambdaGroupClassBuilder.java b/src/main/java/com/android/tools/r8/ir/optimize/lambda/LambdaGroupClassBuilder.java
deleted file mode 100644
index 92f0972..0000000
--- a/src/main/java/com/android/tools/r8/ir/optimize/lambda/LambdaGroupClassBuilder.java
+++ /dev/null
@@ -1,85 +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.ir.optimize.lambda;
-
-import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
-import com.android.tools.r8.graph.AppView;
-import com.android.tools.r8.graph.ClassAccessFlags;
-import com.android.tools.r8.graph.DexAnnotationSet;
-import com.android.tools.r8.graph.DexEncodedField;
-import com.android.tools.r8.graph.DexEncodedMethod;
-import com.android.tools.r8.graph.DexItemFactory;
-import com.android.tools.r8.graph.DexProgramClass;
-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.GenericSignature.ClassSignature;
-import com.android.tools.r8.graph.InnerClassAttribute;
-import com.android.tools.r8.ir.optimize.info.OptimizationFeedback;
-import com.android.tools.r8.origin.SynthesizedOrigin;
-import java.util.Collections;
-import java.util.List;
-
-// Encapsulates lambda group class building logic and separates
-// it from the rest of lambda group functionality.
-public abstract class LambdaGroupClassBuilder<T extends LambdaGroup> {
- protected final T group;
- protected final DexItemFactory factory;
- protected final String origin;
-
- protected LambdaGroupClassBuilder(T group, DexItemFactory factory, String origin) {
- this.group = group;
- this.factory = factory;
- this.origin = origin;
- }
-
- public final DexProgramClass synthesizeClass(
- AppView<? extends AppInfoWithClassHierarchy> appView, OptimizationFeedback feedback) {
- DexType groupClassType = group.getGroupClassType();
- DexType superClassType = getSuperClassType();
- return new DexProgramClass(
- groupClassType,
- null,
- new SynthesizedOrigin(origin, getClass()),
- buildAccessFlags(),
- superClassType,
- buildInterfaces(),
- factory.createString(origin),
- null,
- Collections.emptyList(),
- buildEnclosingMethodAttribute(),
- buildInnerClasses(),
- buildClassSignature(),
- DexAnnotationSet.empty(),
- buildStaticFields(appView, feedback),
- buildInstanceFields(),
- buildDirectMethods(),
- buildVirtualMethods(),
- factory.getSkipNameValidationForTesting(),
- // The name of the class is based on the hash of the content.
- DexProgramClass::checksumFromType);
- }
-
- protected abstract DexType getSuperClassType();
-
- protected abstract ClassAccessFlags buildAccessFlags();
-
- protected abstract EnclosingMethodAttribute buildEnclosingMethodAttribute();
-
- protected abstract List<InnerClassAttribute> buildInnerClasses();
-
- protected abstract ClassSignature buildClassSignature();
-
- protected abstract DexEncodedMethod[] buildVirtualMethods();
-
- protected abstract DexEncodedMethod[] buildDirectMethods();
-
- protected abstract DexEncodedField[] buildInstanceFields();
-
- protected abstract DexEncodedField[] buildStaticFields(
- AppView<? extends AppInfoWithClassHierarchy> appView, OptimizationFeedback feedback);
-
- protected abstract DexTypeList buildInterfaces();
-}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/lambda/LambdaGroupId.java b/src/main/java/com/android/tools/r8/ir/optimize/lambda/LambdaGroupId.java
deleted file mode 100644
index 2c31541..0000000
--- a/src/main/java/com/android/tools/r8/ir/optimize/lambda/LambdaGroupId.java
+++ /dev/null
@@ -1,20 +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.ir.optimize.lambda;
-
-// Represents a lambda group identifier uniquely identifying the groups
-// of potentially mergeable lambdas.
-//
-// Implements hashCode/equals in a way that guarantees that if two lambda
-// classes has equal ids they belong to the same lambda group.
-public interface LambdaGroupId {
- LambdaGroup createGroup();
-
- @Override
- int hashCode();
-
- @Override
- boolean equals(Object obj);
-}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/lambda/LambdaMerger.java b/src/main/java/com/android/tools/r8/ir/optimize/lambda/LambdaMerger.java
deleted file mode 100644
index 5bccf4d4..0000000
--- a/src/main/java/com/android/tools/r8/ir/optimize/lambda/LambdaMerger.java
+++ /dev/null
@@ -1,738 +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.ir.optimize.lambda;
-
-import com.android.tools.r8.DiagnosticsHandler;
-import com.android.tools.r8.errors.Unreachable;
-import com.android.tools.r8.graph.AppView;
-import com.android.tools.r8.graph.DexApplication;
-import com.android.tools.r8.graph.DexApplication.Builder;
-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.DexEncodedMethod;
-import com.android.tools.r8.graph.DexItemFactory;
-import com.android.tools.r8.graph.DexMethod;
-import com.android.tools.r8.graph.DexProgramClass;
-import com.android.tools.r8.graph.DexType;
-import com.android.tools.r8.graph.GraphLens;
-import com.android.tools.r8.graph.ProgramMethod;
-import com.android.tools.r8.graph.ResolutionResult;
-import com.android.tools.r8.graph.classmerging.HorizontallyMergedLambdaClasses;
-import com.android.tools.r8.ir.analysis.type.DestructivePhiTypeUpdater;
-import com.android.tools.r8.ir.code.Argument;
-import com.android.tools.r8.ir.code.IRCode;
-import com.android.tools.r8.ir.code.InitClass;
-import com.android.tools.r8.ir.code.InstanceGet;
-import com.android.tools.r8.ir.code.InstancePut;
-import com.android.tools.r8.ir.code.Instruction;
-import com.android.tools.r8.ir.code.InvokeMethod;
-import com.android.tools.r8.ir.code.InvokeVirtual;
-import com.android.tools.r8.ir.code.NewInstance;
-import com.android.tools.r8.ir.code.Phi;
-import com.android.tools.r8.ir.code.StaticGet;
-import com.android.tools.r8.ir.code.StaticPut;
-import com.android.tools.r8.ir.code.Value;
-import com.android.tools.r8.ir.conversion.IRConverter;
-import com.android.tools.r8.ir.conversion.MethodProcessor;
-import com.android.tools.r8.ir.optimize.Inliner;
-import com.android.tools.r8.ir.optimize.Inliner.ConstraintWithTarget;
-import com.android.tools.r8.ir.optimize.Inliner.InliningInfo;
-import com.android.tools.r8.ir.optimize.info.FieldOptimizationInfo;
-import com.android.tools.r8.ir.optimize.info.MethodOptimizationInfo;
-import com.android.tools.r8.ir.optimize.info.OptimizationFeedback;
-import com.android.tools.r8.ir.optimize.info.OptimizationFeedback.OptimizationInfoFixer;
-import com.android.tools.r8.ir.optimize.inliner.InliningIRProvider;
-import com.android.tools.r8.ir.optimize.lambda.CodeProcessor.Strategy;
-import com.android.tools.r8.ir.optimize.lambda.LambdaGroup.LambdaStructureError;
-import com.android.tools.r8.ir.optimize.lambda.kotlin.KotlinLambdaGroupIdFactory;
-import com.android.tools.r8.kotlin.Kotlin;
-import com.android.tools.r8.shaking.AppInfoWithLiveness;
-import com.android.tools.r8.shaking.FieldAccessInfoCollectionModifier;
-import com.android.tools.r8.shaking.ProguardConfiguration;
-import com.android.tools.r8.utils.SetUtils;
-import com.android.tools.r8.utils.StringDiagnostic;
-import com.android.tools.r8.utils.ThreadUtils;
-import com.android.tools.r8.utils.ThrowingConsumer;
-import com.android.tools.r8.utils.Timing;
-import com.android.tools.r8.utils.collections.LongLivedProgramMethodSetBuilder;
-import com.android.tools.r8.utils.collections.SortedProgramMethodSet;
-import com.google.common.collect.BiMap;
-import com.google.common.collect.HashBiMap;
-import com.google.common.collect.Sets;
-import java.util.ArrayDeque;
-import java.util.ArrayList;
-import java.util.Comparator;
-import java.util.Deque;
-import java.util.IdentityHashMap;
-import java.util.Iterator;
-import java.util.LinkedHashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.Map.Entry;
-import java.util.Set;
-import java.util.concurrent.ExecutionException;
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Future;
-import java.util.function.Function;
-
-// Merging lambda classes into single lambda group classes. There are three flavors
-// of lambdas we are dealing with:
-// (a) lambda classes synthesized in desugaring, handles java lambdas
-// (b) k-style lambda classes synthesized by kotlin compiler
-// (c) j-style lambda classes synthesized by kotlin compiler
-//
-// Lambda merging is potentially applicable to all three of them, but
-// current implementation deals with both k- and j-style lambdas.
-//
-// In general we merge lambdas in 5 phases:
-// 1. collect all lambdas and compute group candidates. we do it synchronously
-// and ensure that the order of lambda groups and lambdas inside each group
-// is stable.
-// 2. analyze usages of lambdas and exclude lambdas with unexpected usage
-// NOTE: currently we consider *all* usages outside the code invalid
-// so we only need to patch method code when replacing the lambda class.
-// 3. exclude (invalidate) all lambda classes with usages we don't understand
-// or support, compact the remaining lambda groups, remove trivial groups
-// with less that 2 lambdas.
-// 4. replace lambda valid/supported class constructions with references to
-// lambda group classes.
-// 5. synthesize group lambda classes.
-//
-public final class LambdaMerger {
-
- private abstract static class Mode {
-
- void rewriteCode(
- ProgramMethod method,
- IRCode code,
- Inliner inliner,
- ProgramMethod context,
- InliningIRProvider provider) {}
-
- void analyzeCode(ProgramMethod method, IRCode code) {}
- }
-
- private class AnalyzeMode extends Mode {
-
- @Override
- void analyzeCode(ProgramMethod method, IRCode code) {
- new AnalysisStrategy(method, code).processCode();
- }
- }
-
- private class ApplyMode extends Mode {
-
- private final Map<DexProgramClass, LambdaGroup> lambdaGroups;
- private final LambdaMergerOptimizationInfoFixer optimizationInfoFixer;
-
- ApplyMode(
- Map<DexProgramClass, LambdaGroup> lambdaGroups,
- LambdaMergerOptimizationInfoFixer optimizationInfoFixer) {
- this.lambdaGroups = lambdaGroups;
- this.optimizationInfoFixer = optimizationInfoFixer;
- }
-
- @Override
- void rewriteCode(
- ProgramMethod method,
- IRCode code,
- Inliner inliner,
- ProgramMethod context,
- InliningIRProvider provider) {
- LambdaGroup lambdaGroup = lambdaGroups.get(method.getHolder());
- if (lambdaGroup == null) {
- // Only rewrite the methods that have not been synthesized for the lambda group classes.
- new ApplyStrategy(method, code, context, optimizationInfoFixer).processCode();
- return;
- }
-
- if (method.getDefinition().isInitializer()) {
- // Should not require rewriting.
- return;
- }
-
- assert method.getDefinition().isNonPrivateVirtualMethod();
- assert context == null;
-
- Map<InvokeVirtual, InliningInfo> invokesToInline = new IdentityHashMap<>();
- for (InvokeVirtual invoke : code.<InvokeVirtual>instructions(Instruction::isInvokeVirtual)) {
- DexMethod invokedMethod = invoke.getInvokedMethod();
- DexType holder = invokedMethod.holder;
- if (lambdaGroup.containsLambda(holder)) {
- // TODO(b/150685763): Check if we can use simpler lookup.
- ResolutionResult resolution = appView.appInfo().resolveMethodOnClass(invokedMethod);
- assert resolution.isSingleResolution();
- ProgramMethod singleTarget =
- resolution.asSingleResolution().getResolutionPair().asProgramMethod();
- assert singleTarget != null;
- invokesToInline.put(invoke, new InliningInfo(singleTarget, singleTarget.getHolderType()));
- }
- }
-
- assert invokesToInline.size() > 1
- || appView.options().testing.verificationSizeLimitInBytesOverride > -1;
-
- inliner.performForcedInlining(method, code, invokesToInline, provider, Timing.empty());
- }
- }
-
- // Maps lambda into a group, only contains lambdas we decided to merge.
- // NOTE: needs synchronization.
- private final Map<DexType, LambdaGroup> lambdas = new IdentityHashMap<>();
- // We use linked map to ensure stable ordering of the groups
- // when they are processed sequentially.
- // NOTE: needs synchronization.
- private final Map<LambdaGroupId, LambdaGroup> groups = new LinkedHashMap<>();
-
- // Since invalidating lambdas may happen concurrently we don't remove invalidated lambdas
- // from groups (and `lambdas`) right away since the ordering may be important. Instead we
- // collect invalidated lambdas and remove them from groups after analysis is done.
- private final Set<DexType> invalidatedLambdas = Sets.newConcurrentHashSet();
-
- // Methods which need to be patched to reference lambda group classes instead of the
- // original lambda classes. The number of methods is expected to be small since there
- // is a 1:1 relation between lambda and method it is defined in (unless such a method
- // was inlined by either kotlinc or r8).
- //
- // Note that we don't track precisely lambda -> method mapping, so it may happen that
- // we mark a method for further processing, and then invalidate the only lambda referenced
- // from it. In this case we will reprocess method that does not need patching, but it
- // should not be happening very frequently and we ignore possible overhead.
- private final LongLivedProgramMethodSetBuilder<SortedProgramMethodSet> methodsToReprocess =
- LongLivedProgramMethodSetBuilder.createForSortedSet();
-
- private final AppView<AppInfoWithLiveness> appView;
- private final Kotlin kotlin;
- private final DiagnosticsHandler reporter;
-
- private Mode mode;
-
- // Lambda visitor invalidating lambdas it sees.
- private final LambdaTypeVisitor lambdaInvalidator;
- // Lambda visitor throwing Unreachable on each lambdas it sees.
- private final LambdaTypeVisitor lambdaChecker;
-
- public LambdaMerger(AppView<AppInfoWithLiveness> appView) {
- DexItemFactory factory = appView.dexItemFactory();
- this.appView = appView;
- this.kotlin = factory.kotlin;
- this.reporter = appView.options().reporter;
-
- this.lambdaInvalidator =
- new LambdaTypeVisitor(factory, this::isMergeableLambda, this::invalidateLambda);
- this.lambdaChecker =
- new LambdaTypeVisitor(
- factory,
- this::isMergeableLambda,
- type -> {
- throw new Unreachable("Unexpected lambda " + type.toSourceString());
- });
- }
-
- private void invalidateLambda(DexType lambda) {
- invalidatedLambdas.add(lambda);
- }
-
- private synchronized boolean isMergeableLambda(DexType lambda) {
- return lambdas.containsKey(lambda);
- }
-
- private synchronized LambdaGroup getLambdaGroup(DexType lambda) {
- return lambdas.get(lambda);
- }
-
- private synchronized void queueForProcessing(ProgramMethod method) {
- methodsToReprocess.add(method);
- }
-
- // Collect all group candidates and assign unique lambda ids inside each group.
- // We do this before methods are being processed to guarantee stable order of
- // lambdas inside each group.
- public final void collectGroupCandidates(DexApplication app) {
- // Collect lambda groups.
- app.classes().stream()
- .filter(cls -> !appView.appInfo().isPinned(cls.type))
- .filter(
- cls ->
- appView.testing().kotlinLambdaMergerFactoryForClass.apply(cls) != null
- && KotlinLambdaGroupIdFactory.hasValidAnnotations(kotlin, cls)
- && !appView.appInfo().getClassToFeatureSplitMap().isInFeature(cls))
- .sorted(Comparator.comparing(DexClass::getType)) // Ensure stable ordering.
- .forEachOrdered(
- lambda -> {
- try {
- KotlinLambdaGroupIdFactory lambdaGroupIdFactory =
- appView.testing().kotlinLambdaMergerFactoryForClass.apply(lambda);
- LambdaGroupId id = lambdaGroupIdFactory.validateAndCreate(appView, kotlin, lambda);
- LambdaGroup group = groups.computeIfAbsent(id, LambdaGroupId::createGroup);
- group.add(lambda);
- lambdas.put(lambda.type, group);
- } catch (LambdaStructureError error) {
- // Intentionally empty.
- }
- });
-
- // Remove trivial groups.
- removeTrivialLambdaGroups();
-
- assert mode == null;
- mode = new AnalyzeMode();
- }
-
- /**
- * Is called by IRConverter::rewriteCode. Performs different actions depending on the current
- * mode.
- *
- * <ol>
- * <li>in ANALYZE mode analyzes invalid usages of lambda classes inside the method code,
- * invalidated such lambda classes, collects methods that need to be patched.
- * <li>in APPLY mode does nothing.
- * </ol>
- */
- public final void analyzeCode(ProgramMethod method, IRCode code) {
- if (mode != null) {
- mode.analyzeCode(method, code);
- }
- }
-
- /**
- * Is called by IRConverter::rewriteCode. Performs different actions depending on the current
- * mode.
- *
- * <ol>
- * <li>in ANALYZE mode does nothing.
- * <li>in APPLY mode patches the code to use lambda group classes, also asserts that there are
- * no more invalid lambda class references.
- * </ol>
- */
- public final void rewriteCode(
- ProgramMethod method, IRCode code, Inliner inliner, MethodProcessor methodProcessor) {
- if (mode != null) {
- mode.rewriteCode(
- code.context(),
- code,
- inliner,
- null,
- new InliningIRProvider(appView, method, code, methodProcessor));
- }
- }
-
- /**
- * Similar to {@link #rewriteCode(ProgramMethod, IRCode, Inliner, MethodProcessor)}, but for
- * rewriting code for inlining. The {@param context} is the caller that {@param method} is being
- * inlined into.
- */
- public final void rewriteCodeForInlining(
- ProgramMethod method, IRCode code, ProgramMethod context, InliningIRProvider provider) {
- if (mode != null) {
- mode.rewriteCode(method, code, null, context, provider);
- }
- }
-
- public final void applyLambdaClassMapping(
- DexApplication app,
- IRConverter converter,
- OptimizationFeedback feedback,
- Builder<?> builder,
- ExecutorService executorService,
- GraphLens appliedGraphLens)
- throws ExecutionException {
- if (lambdas.isEmpty()) {
- appView.setHorizontallyMergedLambdaClasses(HorizontallyMergedLambdaClasses.empty());
- return;
- }
-
- // Analyse references from program classes. We assume that this optimization
- // is only used for full program analysis and there are no classpath classes.
- ThreadUtils.processItems(app.classes(), this::analyzeClass, executorService);
-
- // Analyse more complex aspects of lambda classes including method code.
- analyzeLambdaClassesStructure(executorService);
-
- // Remove invalidated lambdas, compact groups to ensure
- // sequential lambda ids, create group lambda classes.
- BiMap<LambdaGroup, DexProgramClass> lambdaGroupsClasses = finalizeLambdaGroups(feedback);
-
- // Fixup optimization info to ensure that the optimization info does not refer to any merged
- // lambdas.
- LambdaMergerOptimizationInfoFixer optimizationInfoFixer =
- new LambdaMergerOptimizationInfoFixer(lambdaGroupsClasses);
- feedback.fixupOptimizationInfos(appView, executorService, optimizationInfoFixer);
-
- // Switch to APPLY strategy.
- this.mode = new ApplyMode(lambdaGroupsClasses.inverse(), optimizationInfoFixer);
-
- FieldAccessInfoCollectionModifier.Builder fieldAccessInfoCollectionModifierBuilder =
- FieldAccessInfoCollectionModifier.builder();
-
- // Add synthesized lambda group classes to the builder.
- for (Entry<LambdaGroup, DexProgramClass> entry : lambdaGroupsClasses.entrySet()) {
- DexProgramClass synthesizedClass = entry.getValue();
- appView
- .appInfo()
- .addSynthesizedClass(synthesizedClass, entry.getKey().shouldAddToMainDex(appView));
- builder.addSynthesizedClass(synthesizedClass);
-
- synthesizedClass.forEachField(
- field ->
- fieldAccessInfoCollectionModifierBuilder
- .recordFieldReadInUnknownContext(field.getReference())
- .recordFieldWriteInUnknownContext(field.getReference()));
-
- // Eventually, we need to process synthesized methods in the lambda group.
- // Otherwise, abstract SynthesizedCode will be flown to Enqueuer.
- // But that process should not see the holder. Otherwise, lambda calls in the main dispatch
- // method became recursive calls via the lens rewriter. They should remain, then inliner
- // will inline methods from mergee lambdas to the main dispatch method.
- // Then, there is a dilemma: other sub optimizations trigger subtype lookup that will throw
- // NPE if it cannot find the holder for this synthesized lambda group.
- // One hack here is to mark those methods `processed` so that the lens rewriter is skipped.
- synthesizedClass.forEachMethod(
- encodedMethod -> encodedMethod.markProcessed(ConstraintWithTarget.NEVER));
- }
-
- // Record field accesses for synthesized fields.
- fieldAccessInfoCollectionModifierBuilder.build().modify(appView);
-
- converter.optimizeSynthesizedClasses(lambdaGroupsClasses.values(), executorService);
-
- // Rewrite lambda class references into lambda group class
- // references inside methods from the processing queue.
- rewriteLambdaReferences(converter, executorService, appliedGraphLens);
- this.mode = null;
-
- appView.setHorizontallyMergedLambdaClasses(new HorizontallyMergedLambdaClasses(lambdas));
- }
-
- private void analyzeLambdaClassesStructure(ExecutorService service) throws ExecutionException {
- List<Future<?>> futures = new ArrayList<>();
- for (LambdaGroup group : groups.values()) {
- ThrowingConsumer<DexClass, LambdaStructureError> validator =
- group.lambdaClassValidator(kotlin, appView.appInfo());
- group.forEachLambda(
- info ->
- futures.add(
- service.submit(
- () -> {
- try {
- validator.accept(info.clazz);
- } catch (LambdaStructureError error) {
- ProguardConfiguration proguardConfiguration =
- appView.options().getProguardConfiguration();
- if (error.reportable
- && !proguardConfiguration
- .getDontNotePatterns()
- .matches(info.clazz.getType())) {
- reporter.info(
- new StringDiagnostic(
- "Unexpected Kotlin lambda structure ["
- + info.clazz.type.toSourceString()
- + "]: "
- + error.getMessage()));
- }
- invalidateLambda(info.clazz.type);
- }
- })));
- }
- ThreadUtils.awaitFutures(futures);
- }
-
- private BiMap<LambdaGroup, DexProgramClass> finalizeLambdaGroups(OptimizationFeedback feedback) {
- for (DexType lambda : invalidatedLambdas) {
- LambdaGroup group = lambdas.get(lambda);
- assert group != null;
- lambdas.remove(lambda);
- group.remove(lambda);
- }
- invalidatedLambdas.clear();
-
- // Remove new trivial lambdas.
- removeTrivialLambdaGroups();
-
- // Compact lambda groups, synthesize lambda group classes.
- BiMap<LambdaGroup, DexProgramClass> result = HashBiMap.create();
- for (LambdaGroup group : groups.values()) {
- assert !group.isTrivial() : "No trivial group is expected here.";
- group.compact();
- DexProgramClass lambdaGroupClass = group.synthesizeClass(appView, feedback);
- result.put(group, lambdaGroupClass);
- }
- return result;
- }
-
- private void removeTrivialLambdaGroups() {
- Iterator<Entry<LambdaGroupId, LambdaGroup>> iterator = groups.entrySet().iterator();
- while (iterator.hasNext()) {
- Entry<LambdaGroupId, LambdaGroup> group = iterator.next();
- if (group.getValue().isTrivial()) {
- iterator.remove();
- assert group.getValue().size() < 2;
- group.getValue().forEachLambda(info -> this.lambdas.remove(info.clazz.type));
- }
- }
- }
-
- private void rewriteLambdaReferences(
- IRConverter converter, ExecutorService executorService, GraphLens appliedGraphLens)
- throws ExecutionException {
- if (methodsToReprocess.isEmpty()) {
- return;
- }
- SortedProgramMethodSet methods = methodsToReprocess.build(appView, appliedGraphLens);
- converter.processMethodsConcurrently(methods, executorService);
- assert methods.stream()
- .map(DexClassAndMethod::getDefinition)
- .allMatch(DexEncodedMethod::isProcessed);
- }
-
- private void analyzeClass(DexProgramClass clazz) {
- lambdaInvalidator.accept(clazz.superType);
- lambdaInvalidator.accept(clazz.interfaces);
- lambdaInvalidator.accept(clazz.annotations());
-
- for (DexEncodedField field : clazz.staticFields()) {
- lambdaInvalidator.accept(field.annotations());
- if (field.field.type != clazz.type) {
- // Ignore static fields of the same type.
- lambdaInvalidator.accept(field.field, clazz.type);
- }
- }
- for (DexEncodedField field : clazz.instanceFields()) {
- lambdaInvalidator.accept(field.annotations());
- lambdaInvalidator.accept(field.field, clazz.type);
- }
-
- for (DexEncodedMethod method : clazz.methods()) {
- lambdaInvalidator.accept(method.annotations());
- lambdaInvalidator.accept(method.parameterAnnotationsList);
- lambdaInvalidator.accept(method.method, clazz.type);
- }
- }
-
- private Strategy strategyProvider(DexType type) {
- LambdaGroup group = this.getLambdaGroup(type);
- return group != null ? group.getCodeStrategy() : CodeProcessor.NoOp;
- }
-
- private final class AnalysisStrategy extends CodeProcessor {
- private AnalysisStrategy(ProgramMethod method, IRCode code) {
- super(
- LambdaMerger.this.appView,
- LambdaMerger.this::strategyProvider,
- lambdaInvalidator,
- method,
- code);
- }
-
- @Override
- void process(Strategy strategy, InvokeMethod invokeMethod) {
- queueForProcessing(method);
- }
-
- @Override
- void process(Strategy strategy, NewInstance newInstance) {
- queueForProcessing(method);
- }
-
- @Override
- void process(Strategy strategy, InstancePut instancePut) {
- queueForProcessing(method);
- }
-
- @Override
- void process(Strategy strategy, InstanceGet instanceGet) {
- queueForProcessing(method);
- }
-
- @Override
- void process(Strategy strategy, StaticPut staticPut) {
- queueForProcessing(method);
- }
-
- @Override
- void process(Strategy strategy, StaticGet staticGet) {
- queueForProcessing(method);
- }
-
- @Override
- void process(Strategy strategy, InitClass initClass) {
- queueForProcessing(method);
- }
-
- @Override
- void process(Strategy strategy, Argument argument) {
- throw new Unreachable();
- }
- }
-
- public final class ApplyStrategy extends CodeProcessor {
-
- private final LambdaMergerOptimizationInfoFixer optimizationInfoFixer;
-
- private final Set<Value> typeAffectedValues = Sets.newIdentityHashSet();
-
- private ApplyStrategy(
- ProgramMethod method,
- IRCode code,
- ProgramMethod context,
- LambdaMergerOptimizationInfoFixer optimizationInfoFixer) {
- super(
- LambdaMerger.this.appView,
- LambdaMerger.this::strategyProvider,
- lambdaChecker,
- method,
- code,
- context);
- this.optimizationInfoFixer = optimizationInfoFixer;
- }
-
- public void recordTypeHasChanged(Value value) {
- for (Value affectedValue : value.affectedValues()) {
- if (typeMayHaveChanged(affectedValue)) {
- typeAffectedValues.add(affectedValue);
- }
- }
- }
-
- @Override
- void processCode() {
- super.processCode();
-
- if (typeAffectedValues.isEmpty()) {
- return;
- }
-
- // Find all the transitively type affected values.
- Set<Value> transitivelyTypeAffectedValues = SetUtils.newIdentityHashSet(typeAffectedValues);
- Deque<Value> worklist = new ArrayDeque<>(typeAffectedValues);
- while (!worklist.isEmpty()) {
- Value value = worklist.pop();
- assert typeMayHaveChanged(value);
- assert transitivelyTypeAffectedValues.contains(value);
-
- for (Value affectedValue : value.affectedValues()) {
- if (typeMayHaveChanged(affectedValue)
- && transitivelyTypeAffectedValues.add(affectedValue)) {
- worklist.add(affectedValue);
- }
- }
- }
-
- // Update the types of these values if they refer to obsolete types. This is needed to be
- // able to propagate the type information correctly, since lambda merging is neither a
- // narrowing nor a widening.
- for (Value value : transitivelyTypeAffectedValues) {
- value.setType(value.getType().fixupClassTypeReferences(optimizationInfoFixer, appView));
- }
-
- // Filter out the type affected phis and destructively update the type of the phis. This is
- // needed because narrowing does not work in presence of cyclic phis.
- Set<Phi> typeAffectedPhis = Sets.newIdentityHashSet();
- for (Value typeAffectedValue : transitivelyTypeAffectedValues) {
- if (typeAffectedValue.isPhi()) {
- typeAffectedPhis.add(typeAffectedValue.asPhi());
- }
- }
- if (!typeAffectedPhis.isEmpty()) {
- new DestructivePhiTypeUpdater(appView, optimizationInfoFixer)
- .recomputeAndPropagateTypes(code, typeAffectedPhis);
- }
- assert code.verifyTypes(appView);
- }
-
- private boolean typeMayHaveChanged(Value value) {
- return value.isPhi() || !value.definition.hasInvariantOutType();
- }
-
- @Override
- void process(Strategy strategy, InvokeMethod invokeMethod) {
- strategy.patch(this, invokeMethod);
- }
-
- @Override
- void process(Strategy strategy, NewInstance newInstance) {
- strategy.patch(this, newInstance);
- }
-
- @Override
- void process(Strategy strategy, InstancePut instancePut) {
- // Instance put should only appear in lambda class instance constructor,
- // we should never get here since we never rewrite them.
- throw new Unreachable();
- }
-
- @Override
- void process(Strategy strategy, InstanceGet instanceGet) {
- strategy.patch(this, instanceGet);
- }
-
- @Override
- void process(Strategy strategy, StaticPut staticPut) {
- // Static put should only appear in lambda class static initializer,
- // we should never get here since we never rewrite them.
- throw new Unreachable();
- }
-
- @Override
- void process(Strategy strategy, StaticGet staticGet) {
- strategy.patch(this, staticGet);
- }
-
- @Override
- void process(Strategy strategy, InitClass initClass) {
- strategy.patch(this, initClass);
- }
-
- @Override
- void process(Strategy strategy, Argument argument) {
- strategy.patch(this, argument);
- }
- }
-
- private final class LambdaMergerOptimizationInfoFixer
- implements Function<DexType, DexType>, OptimizationInfoFixer {
-
- private final Map<LambdaGroup, DexProgramClass> lambdaGroupsClasses;
-
- LambdaMergerOptimizationInfoFixer(Map<LambdaGroup, DexProgramClass> lambdaGroupsClasses) {
- this.lambdaGroupsClasses = lambdaGroupsClasses;
- }
-
- @Override
- public DexType apply(DexType type) {
- LambdaGroup group = lambdas.get(type);
- if (group != null) {
- DexProgramClass clazz = lambdaGroupsClasses.get(group);
- if (clazz != null) {
- return clazz.type;
- }
- }
- return type;
- }
-
- @Override
- public void fixup(DexEncodedField field) {
- FieldOptimizationInfo optimizationInfo = field.getOptimizationInfo();
- if (optimizationInfo.isMutableFieldOptimizationInfo()) {
- optimizationInfo.asMutableFieldOptimizationInfo().fixupClassTypeReferences(this, appView);
- } else {
- assert optimizationInfo.isDefaultFieldOptimizationInfo();
- }
- }
-
- @Override
- public void fixup(DexEncodedMethod method) {
- MethodOptimizationInfo optimizationInfo = method.getOptimizationInfo();
- if (optimizationInfo.isUpdatableMethodOptimizationInfo()) {
- optimizationInfo
- .asUpdatableMethodOptimizationInfo()
- .fixupClassTypeReferences(this, appView);
- } else {
- assert optimizationInfo.isDefaultMethodOptimizationInfo();
- }
- }
- }
-}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/lambda/LambdaTypeVisitor.java b/src/main/java/com/android/tools/r8/ir/optimize/lambda/LambdaTypeVisitor.java
deleted file mode 100644
index 9dce489..0000000
--- a/src/main/java/com/android/tools/r8/ir/optimize/lambda/LambdaTypeVisitor.java
+++ /dev/null
@@ -1,146 +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.ir.optimize.lambda;
-
-import com.android.tools.r8.graph.DexAnnotation;
-import com.android.tools.r8.graph.DexAnnotationElement;
-import com.android.tools.r8.graph.DexAnnotationSet;
-import com.android.tools.r8.graph.DexCallSite;
-import com.android.tools.r8.graph.DexEncodedAnnotation;
-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.DexMethodHandle;
-import com.android.tools.r8.graph.DexProto;
-import com.android.tools.r8.graph.DexType;
-import com.android.tools.r8.graph.DexTypeList;
-import com.android.tools.r8.graph.DexValue;
-import com.android.tools.r8.graph.ParameterAnnotationsList;
-import java.util.function.Consumer;
-import java.util.function.Predicate;
-
-// Encapsulates the logic of visiting all lambda classes.
-final class LambdaTypeVisitor {
- private final DexItemFactory factory;
- private final Predicate<DexType> isLambdaType;
- private final Consumer<DexType> onLambdaType;
-
- LambdaTypeVisitor(DexItemFactory factory,
- Predicate<DexType> isLambdaType, Consumer<DexType> onLambdaType) {
- this.factory = factory;
- this.isLambdaType = isLambdaType;
- this.onLambdaType = onLambdaType;
- }
-
- void accept(DexCallSite callSite) {
- accept(callSite.methodProto);
- accept(callSite.bootstrapMethod);
- for (DexValue value : callSite.bootstrapArgs) {
- accept(value);
- }
- }
-
- private void accept(DexValue value) {
- switch (value.getValueKind()) {
- case ARRAY:
- for (DexValue elementValue : value.asDexValueArray().getValues()) {
- accept(elementValue);
- }
- break;
- case FIELD:
- accept(value.asDexValueField().value, null);
- break;
- case METHOD:
- accept(value.asDexValueMethod().value, null);
- break;
- case METHOD_HANDLE:
- accept(value.asDexValueMethodHandle().value);
- break;
- case METHOD_TYPE:
- accept(value.asDexValueMethodType().value);
- break;
- case TYPE:
- accept(value.asDexValueType().value);
- break;
- default:
- // Intentionally empty.
- }
- }
-
- void accept(DexMethodHandle handle) {
- if (handle.isFieldHandle()) {
- accept(handle.asField(), null);
- } else {
- assert handle.isMethodHandle();
- accept(handle.asMethod(), null);
- }
- }
-
- void accept(DexField field, DexType holderToIgnore) {
- accept(field.type);
- if (holderToIgnore != field.holder) {
- accept(field.holder);
- }
- }
-
- void accept(DexMethod method, DexType holderToIgnore) {
- if (holderToIgnore != method.holder) {
- accept(method.holder);
- }
- accept(method.proto);
- }
-
- void accept(DexProto proto) {
- accept(proto.returnType);
- accept(proto.parameters);
- }
-
- void accept(DexTypeList types) {
- for (DexType type : types.values) {
- accept(type);
- }
- }
-
- void accept(DexAnnotationSet annotationSet) {
- for (DexAnnotation annotation : annotationSet.annotations) {
- accept(annotation);
- }
- }
-
- void accept(ParameterAnnotationsList parameterAnnotationsList) {
- parameterAnnotationsList.forEachAnnotation(this::accept);
- }
-
- private void accept(DexAnnotation annotation) {
- accept(annotation.annotation);
- }
-
- private void accept(DexEncodedAnnotation annotation) {
- accept(annotation.type);
- for (DexAnnotationElement element : annotation.elements) {
- accept(element);
- }
- }
-
- private void accept(DexAnnotationElement element) {
- accept(element.value);
- }
-
- void accept(DexType type) {
- if (type == null) {
- return;
- }
- if (type.isPrimitiveType() || type.isVoidType() || type.isPrimitiveArrayType()) {
- return;
- }
- if (type.isArrayType()) {
- accept(type.toArrayElementType(factory));
- return;
- }
- if (isLambdaType.test(type)) {
- onLambdaType.accept(type);
- }
- }
-}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/ClassInitializerSourceCode.java b/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/ClassInitializerSourceCode.java
deleted file mode 100644
index 0eec9af..0000000
--- a/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/ClassInitializerSourceCode.java
+++ /dev/null
@@ -1,69 +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.ir.optimize.lambda.kotlin;
-
-import com.android.tools.r8.graph.DexItemFactory;
-import com.android.tools.r8.graph.DexMethod;
-import com.android.tools.r8.graph.DexType;
-import com.android.tools.r8.graph.DexTypeList;
-import com.android.tools.r8.ir.analysis.type.TypeElement;
-import com.android.tools.r8.ir.code.Invoke.Type;
-import com.android.tools.r8.ir.code.Position;
-import com.android.tools.r8.ir.code.ValueType;
-import com.android.tools.r8.ir.conversion.IRBuilder;
-import com.android.tools.r8.ir.synthetic.SyntheticSourceCode;
-import com.google.common.collect.Lists;
-import java.util.List;
-
-final class ClassInitializerSourceCode extends SyntheticSourceCode {
- private final DexItemFactory factory;
- private final KotlinLambdaGroup group;
-
- ClassInitializerSourceCode(
- DexMethod method, DexItemFactory factory, KotlinLambdaGroup group, Position callerPosition) {
- super(null, method, callerPosition);
- assert method.proto.returnType == factory.voidType;
- assert method.proto.parameters == DexTypeList.empty();
- this.factory = factory;
- this.group = group;
- }
-
- @Override
- protected void prepareInstructions() {
- DexType groupClassType = group.getGroupClassType();
- DexMethod lambdaConstructorMethod = factory.createMethod(groupClassType,
- factory.createProto(factory.voidType, factory.intType), factory.constructorMethodName);
-
- int instance = nextRegister(ValueType.OBJECT);
- int lambdaId = nextRegister(ValueType.INT);
- List<ValueType> argTypes = Lists.newArrayList(ValueType.OBJECT, ValueType.INT);
- List<Integer> argRegisters = Lists.newArrayList(instance, lambdaId);
-
- group.forEachLambda(
- info -> {
- DexType lambda = info.clazz.type;
- if (group.isSingletonLambda(lambda)) {
- int id = group.lambdaId(lambda);
- add(builder -> builder.addNewInstance(instance, groupClassType));
- add(builder -> builder.addConst(TypeElement.getInt(), lambdaId, id));
- add(
- builder ->
- builder.addInvoke(
- Type.DIRECT,
- lambdaConstructorMethod,
- lambdaConstructorMethod.proto,
- argTypes,
- argRegisters,
- false /* isInterface*/));
- add(
- builder ->
- builder.addStaticPut(instance, group.getSingletonInstanceField(factory, id)));
- }
- });
-
- assert this.nextInstructionIndex() > 0 : "no single field initialized";
- add(IRBuilder::addReturn);
- }
-}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/JStyleLambdaGroup.java b/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/JStyleLambdaGroup.java
deleted file mode 100644
index 117ee3b..0000000
--- a/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/JStyleLambdaGroup.java
+++ /dev/null
@@ -1,238 +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.ir.optimize.lambda.kotlin;
-
-import com.android.tools.r8.code.ReturnVoid;
-import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
-import com.android.tools.r8.graph.AppView;
-import com.android.tools.r8.graph.DexClass;
-import com.android.tools.r8.graph.DexEncodedField;
-import com.android.tools.r8.graph.DexEncodedMethod;
-import com.android.tools.r8.graph.DexField;
-import com.android.tools.r8.graph.DexItemFactory;
-import com.android.tools.r8.graph.DexMethod;
-import com.android.tools.r8.graph.DexType;
-import com.android.tools.r8.graph.EnclosingMethodAttribute;
-import com.android.tools.r8.graph.InnerClassAttribute;
-import com.android.tools.r8.ir.code.Invoke.Type;
-import com.android.tools.r8.ir.code.Position;
-import com.android.tools.r8.ir.code.ValueType;
-import com.android.tools.r8.ir.optimize.lambda.LambdaGroup;
-import com.android.tools.r8.ir.synthetic.SyntheticSourceCode;
-import com.android.tools.r8.kotlin.Kotlin;
-import com.android.tools.r8.shaking.AppInfoWithLiveness;
-import com.android.tools.r8.utils.InternalOptions;
-import com.android.tools.r8.utils.ThrowingConsumer;
-import com.google.common.collect.Lists;
-import java.util.List;
-import java.util.function.IntFunction;
-
-// Represents a j-style lambda group created to combine several lambda classes
-// generated by kotlin compiler for kotlin lambda expressions passed to java receivers,
-// like:
-//
-// --- Java --------------------------------------------------------------
-// public static void acceptString(Supplier<String> s) {
-// // ...
-// }
-// -----------------------------------------------------------------------
-// acceptString({ "A" })
-// -----------------------------------------------------------------------
-//
-// Regular stateless j-style lambda class structure looks like below:
-// NOTE: stateless j-style lambdas do not always have INSTANCE field.
-//
-// -----------------------------------------------------------------------------------------------
-// signature <T:Ljava/lang/Object;>Ljava/lang/Object;
-// Ljava/util/function/Supplier<Ljava/lang/String;>;
-// final class lambdas/LambdasKt$foo$4 implements java/util/function/Supplier {
-//
-// public synthetic bridge get()Ljava/lang/Object;
-//
-// public final get()Ljava/lang/String;
-// @Lorg/jetbrains/annotations/NotNull;() // invisible
-//
-// <init>()V
-//
-// public final static Llambdas/LambdasKt$foo$4; INSTANCE
-//
-// static <clinit>()V
-//
-// OUTERCLASS lambdas/LambdasKt foo (Ljava/lang/String;I)Lkotlin/jvm/functions/Function0;
-// final static INNERCLASS lambdas/LambdasKt$foo$4 null null
-// }
-// -----------------------------------------------------------------------------------------------
-//
-// Regular stateful j-style lambda class structure looks like below:
-//
-// -----------------------------------------------------------------------------------------------
-// signature <T:Ljava/lang/Object;>
-// Ljava/lang/Object;Ljava/util/function/Supplier<Ljava/lang/String;>;
-// declaration: lambdas/LambdasKt$foo$5<T> implements java.util.function.Supplier<java.lang.String>
-// final class lambdas/LambdasKt$foo$5 implements java/util/function/Supplier {
-//
-// public synthetic bridge get()Ljava/lang/Object;
-//
-// public final get()Ljava/lang/String;
-// @Lorg/jetbrains/annotations/NotNull;() // invisible
-//
-// <init>(Ljava/lang/String;I)V
-//
-// final synthetic Ljava/lang/String; $m
-// final synthetic I $v
-//
-// OUTERCLASS lambdas/LambdasKt foo (Ljava/lang/String;I)Lkotlin/jvm/functions/Function0;
-// final static INNERCLASS lambdas/LambdasKt$foo$5 null null
-// }
-// -----------------------------------------------------------------------------------------------
-//
-// Key j-style lambda class details:
-// - extends java.lang.Object
-// - implements *any* functional interface (Kotlin does not seem to support scenarios when
-// lambda can implement multiple interfaces).
-// - lambda class is created as an anonymous inner class
-// - lambda class carries generic signature and kotlin metadata attribute
-// - class instance fields represent captured values and have an instance constructor
-// with matching parameters initializing them (see the second class above)
-// - stateless lambda *may* be implemented as a singleton with a static field storing the
-// only instance and initialized in static class constructor (see the first class above)
-// - main lambda method usually matches an exact lambda signature and may have
-// generic signature attribute and nullability parameter annotations
-// - optional bridge method created to satisfy interface implementation and
-// forwarding call to lambda main method
-//
-final class JStyleLambdaGroup extends KotlinLambdaGroup {
- private JStyleLambdaGroup(GroupId id) {
- super(id);
- }
-
- @Override
- protected ClassBuilder getBuilder(DexItemFactory factory, InternalOptions options) {
- return new ClassBuilder(factory, options, "java-style lambda group");
- }
-
- @Override
- public ThrowingConsumer<DexClass, LambdaStructureError> lambdaClassValidator(
- Kotlin kotlin, AppInfoWithClassHierarchy appInfo) {
- return new ClassValidator(kotlin, appInfo);
- }
-
- @Override
- protected String getGroupSuffix() {
- return "js$";
- }
-
- // Specialized group id.
- final static class GroupId extends KotlinLambdaGroupId {
- GroupId(
- AppView<AppInfoWithLiveness> appView,
- String capture,
- DexType iface,
- String pkg,
- String signature,
- DexEncodedMethod mainMethod,
- InnerClassAttribute inner,
- EnclosingMethodAttribute enclosing) {
- super(appView, capture, iface, pkg, signature, mainMethod, inner, enclosing);
- }
-
- @Override
- public boolean equals(Object obj) {
- return obj instanceof GroupId && computeEquals((KotlinLambdaGroupId) obj);
- }
-
- @Override
- String getLambdaKindDescriptor() {
- return "Kotlin j-style lambda group";
- }
-
- @Override
- public LambdaGroup createGroup() {
- return new JStyleLambdaGroup(this);
- }
- }
-
- // Specialized class validator.
- private class ClassValidator extends KotlinLambdaClassValidator {
- ClassValidator(Kotlin kotlin, AppInfoWithClassHierarchy appInfo) {
- super(kotlin, JStyleLambdaGroup.this, appInfo);
- }
-
- @Override
- int getInstanceInitializerMaxSize(List<DexEncodedField> captures) {
- return captures.size() + 2;
- }
-
- @Override
- int validateInstanceInitializerEpilogue(
- com.android.tools.r8.code.Instruction[] instructions, int index)
- throws LambdaStructureError {
- if (!(instructions[index] instanceof com.android.tools.r8.code.InvokeDirect
- || instructions[index] instanceof com.android.tools.r8.code.InvokeDirectRange)
- || instructions[index].getMethod() != kotlin.factory.objectMembers.constructor) {
- throw structureError(LAMBDA_INIT_CODE_VERIFICATION_FAILED);
- }
- index++;
- if (!(instructions[index] instanceof ReturnVoid)) {
- throw structureError(LAMBDA_INIT_CODE_VERIFICATION_FAILED);
- }
- return index + 1;
- }
- }
-
- // Specialized class builder.
- private final class ClassBuilder extends KotlinLambdaGroupClassBuilder<JStyleLambdaGroup> {
- ClassBuilder(DexItemFactory factory, InternalOptions options, String origin) {
- super(JStyleLambdaGroup.this, factory, options, origin);
- }
-
- @Override
- protected DexType getSuperClassType() {
- return factory.objectType;
- }
-
- @Override
- SyntheticSourceCode createInstanceInitializerSourceCode(
- DexType groupClassType, DexMethod initializerMethod, Position callerPosition) {
- return new InstanceInitializerSourceCode(
- factory,
- groupClassType,
- group.getLambdaIdField(factory),
- id -> group.getCaptureField(factory, id),
- initializerMethod,
- callerPosition);
- }
- }
-
- // Specialized instance initializer code.
- private static final class InstanceInitializerSourceCode
- extends KotlinInstanceInitializerSourceCode {
- private final DexMethod objectInitializer;
-
- InstanceInitializerSourceCode(
- DexItemFactory factory,
- DexType lambdaGroupType,
- DexField idField,
- IntFunction<DexField> fieldGenerator,
- DexMethod method,
- Position callerPosition) {
- super(lambdaGroupType, idField, fieldGenerator, method, callerPosition);
- this.objectInitializer = factory.objectMembers.constructor;
- }
-
- @Override
- void prepareSuperConstructorCall(int receiverRegister) {
- add(
- builder ->
- builder.addInvoke(
- Type.DIRECT,
- objectInitializer,
- objectInitializer.proto,
- Lists.newArrayList(ValueType.OBJECT),
- Lists.newArrayList(receiverRegister),
- false /* isInterface */));
- }
- }
-}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/JStyleLambdaGroupIdFactory.java b/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/JStyleLambdaGroupIdFactory.java
deleted file mode 100644
index df72677..0000000
--- a/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/JStyleLambdaGroupIdFactory.java
+++ /dev/null
@@ -1,84 +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.ir.optimize.lambda.kotlin;
-
-import com.android.tools.r8.graph.AppView;
-import com.android.tools.r8.graph.ClassAccessFlags;
-import com.android.tools.r8.graph.DexClass;
-import com.android.tools.r8.graph.DexEncodedMethod;
-import com.android.tools.r8.graph.DexType;
-import com.android.tools.r8.graph.InnerClassAttribute;
-import com.android.tools.r8.ir.optimize.lambda.LambdaGroup.LambdaStructureError;
-import com.android.tools.r8.ir.optimize.lambda.LambdaGroupId;
-import com.android.tools.r8.kotlin.Kotlin;
-import com.android.tools.r8.shaking.AppInfoWithLiveness;
-
-public final class JStyleLambdaGroupIdFactory extends KotlinLambdaGroupIdFactory {
- private static final JStyleLambdaGroupIdFactory INSTANCE = new JStyleLambdaGroupIdFactory();
-
- private JStyleLambdaGroupIdFactory() {}
-
- public static JStyleLambdaGroupIdFactory getInstance() {
- return INSTANCE;
- }
-
- @Override
- public LambdaGroupId validateAndCreate(
- AppView<AppInfoWithLiveness> appView, Kotlin kotlin, DexClass lambda)
- throws LambdaStructureError {
- boolean accessRelaxed =
- appView.options().getProguardConfiguration().isAccessModificationAllowed();
-
- // Ignore ACC_SUPER.
- ClassAccessFlags copy = lambda.accessFlags.copy();
- copy.unsetSuper();
- checkAccessFlags("class access flags", copy, PUBLIC_LAMBDA_CLASS_FLAGS, LAMBDA_CLASS_FLAGS);
-
- // Class and interface.
- validateSuperclass(kotlin, lambda);
- DexType iface = validateInterfaces(kotlin, lambda);
-
- validateStaticFields(kotlin, lambda);
- String captureSignature = validateInstanceFields(lambda, accessRelaxed);
- validateDirectMethods(lambda);
- DexEncodedMethod mainMethod = validateVirtualMethods(lambda);
- String genericSignature = validateAnnotations(appView, kotlin, lambda);
- InnerClassAttribute innerClass = validateInnerClasses(lambda);
-
- return new JStyleLambdaGroup.GroupId(
- appView,
- captureSignature,
- iface,
- accessRelaxed ? "" : lambda.type.getPackageDescriptor(),
- genericSignature,
- mainMethod,
- innerClass,
- lambda.getEnclosingMethodAttribute());
- }
-
- @Override
- void validateSuperclass(Kotlin kotlin, DexClass lambda) throws LambdaStructureError {
- if (lambda.superType != kotlin.factory.objectType) {
- throw new LambdaStructureError("implements " + lambda.superType.toSourceString() +
- " instead of java.lang.Object");
- }
- }
-
- @Override
- DexType validateInterfaces(Kotlin kotlin, DexClass lambda) throws LambdaStructureError {
- if (lambda.interfaces.size() == 0) {
- throw new LambdaStructureError("does not implement any interfaces");
- }
- if (lambda.interfaces.size() > 1) {
- throw new LambdaStructureError(
- "implements more than one interface: " + lambda.interfaces.size());
- }
-
- // We don't validate that the interface is actually a functional interface,
- // since it may be desugared, or optimized in any other way which should not
- // affect lambda class merging.
- return lambda.interfaces.values[0];
- }
-}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/KStyleLambdaGroup.java b/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/KStyleLambdaGroup.java
deleted file mode 100644
index cfae611..0000000
--- a/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/KStyleLambdaGroup.java
+++ /dev/null
@@ -1,256 +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.ir.optimize.lambda.kotlin;
-
-import com.android.tools.r8.code.Const16;
-import com.android.tools.r8.code.Const4;
-import com.android.tools.r8.code.ReturnVoid;
-import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
-import com.android.tools.r8.graph.AppView;
-import com.android.tools.r8.graph.DexClass;
-import com.android.tools.r8.graph.DexEncodedField;
-import com.android.tools.r8.graph.DexEncodedMethod;
-import com.android.tools.r8.graph.DexField;
-import com.android.tools.r8.graph.DexItemFactory;
-import com.android.tools.r8.graph.DexMethod;
-import com.android.tools.r8.graph.DexType;
-import com.android.tools.r8.graph.EnclosingMethodAttribute;
-import com.android.tools.r8.graph.InnerClassAttribute;
-import com.android.tools.r8.ir.analysis.type.TypeElement;
-import com.android.tools.r8.ir.code.Invoke.Type;
-import com.android.tools.r8.ir.code.Position;
-import com.android.tools.r8.ir.code.ValueType;
-import com.android.tools.r8.ir.optimize.lambda.LambdaGroup;
-import com.android.tools.r8.ir.synthetic.SyntheticSourceCode;
-import com.android.tools.r8.kotlin.Kotlin;
-import com.android.tools.r8.shaking.AppInfoWithLiveness;
-import com.android.tools.r8.utils.InternalOptions;
-import com.android.tools.r8.utils.ThrowingConsumer;
-import com.google.common.collect.Lists;
-import java.util.List;
-import java.util.function.IntFunction;
-
-// Represents a k-style lambda group created to combine several lambda classes
-// generated by kotlin compiler for regular kotlin lambda expressions, like:
-//
-// -----------------------------------------------------------------------
-// fun foo(m: String, v: Int): () -> String {
-// val lambda: (String, Int) -> String = { s, i -> s.substring(i) }
-// return { "$m: $v" }
-// }
-// -----------------------------------------------------------------------
-//
-// Regular stateless k-style lambda class structure looks like below:
-// NOTE: stateless k-style lambdas do not always have INSTANCE field.
-//
-// -----------------------------------------------------------------------------------------------
-// // signature Lkotlin/jvm/internal/Lambda;Lkotlin/jvm/functions/Function2<
-// Ljava/lang/String;Ljava/lang/Integer;Ljava/lang/String;>;
-// final class lambdas/LambdasKt$foo$lambda1$1
-// extends kotlin/jvm/internal/Lambda
-// implements kotlin/jvm/functions/Function2 {
-//
-// public synthetic bridge invoke(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
-//
-// public final invoke(Ljava/lang/String;I)Ljava/lang/String;
-// @Lorg/jetbrains/annotations/NotNull;() // invisible
-// @Lorg/jetbrains/annotations/NotNull;() // invisible, parameter 0
-//
-// <init>()V
-//
-// public final static Llambdas/LambdasKt$foo$lambda1$1; INSTANCE
-//
-// static <clinit>()V
-//
-// OUTERCLASS lambdas/LambdasKt foo (Ljava/lang/String;I)Lkotlin/jvm/functions/Function0;
-// final static INNERCLASS lambdas/LambdasKt$foo$lambda1$1 null null
-// }
-// -----------------------------------------------------------------------------------------------
-//
-// Regular stateful k-style lambda class structure looks like below:
-//
-// -----------------------------------------------------------------------------------------------
-// // signature Lkotlin/jvm/internal/Lambda;Lkotlin/jvm/functions/Function0<Ljava/lang/String;>;
-// final class lambdas/LambdasKt$foo$1
-// extends kotlin/jvm/internal/Lambda
-// implements kotlin/jvm/functions/Function0 {
-//
-// public synthetic bridge invoke()Ljava/lang/Object;
-//
-// public final invoke()Ljava/lang/String;
-// @Lorg/jetbrains/annotations/NotNull;() // invisible
-//
-// <init>(Ljava/lang/String;I)V
-//
-// final synthetic Ljava/lang/String; $m
-// final synthetic I $v
-//
-// OUTERCLASS lambdas/LambdasKt foo (Ljava/lang/String;I)Lkotlin/jvm/functions/Function0;
-// final static INNERCLASS lambdas/LambdasKt$foo$1 null null
-// }
-// -----------------------------------------------------------------------------------------------
-//
-// Key k-style lambda class details:
-// - extends kotlin.jvm.internal.Lambda
-// - implements one of kotlin.jvm.functions.Function0..Function22, or FunctionN
-// see: https://github.com/JetBrains/kotlin/blob/master/libraries/
-// stdlib/jvm/runtime/kotlin/jvm/functions/Functions.kt
-// and: https://github.com/JetBrains/kotlin/blob/master/spec-docs/function-types.md
-// - lambda class is created as an anonymous inner class
-// - lambda class carries generic signature and kotlin metadata attribute
-// - class instance fields represent captured values and have an instance constructor
-// with matching parameters initializing them (see the second class above)
-// - stateless lambda *may* be implemented as a singleton with a static field storing the
-// only instance and initialized in static class constructor (see the first class above)
-// - main lambda method usually matches an exact lambda signature and may have
-// generic signature attribute and nullability parameter annotations
-// - optional bridge method created to satisfy interface implementation and
-// forwarding call to lambda main method
-//
-final class KStyleLambdaGroup extends KotlinLambdaGroup {
- private KStyleLambdaGroup(GroupId id) {
- super(id);
- }
-
- @Override
- protected ClassBuilder getBuilder(DexItemFactory factory, InternalOptions options) {
- return new ClassBuilder(factory, options, "kotlin-style lambda group");
- }
-
- @Override
- public ThrowingConsumer<DexClass, LambdaStructureError> lambdaClassValidator(
- Kotlin kotlin, AppInfoWithClassHierarchy appInfo) {
- return new ClassValidator(kotlin, appInfo);
- }
-
- @Override
- protected String getGroupSuffix() {
- return "ks$";
- }
-
- // Specialized group id.
- final static class GroupId extends KotlinLambdaGroupId {
- GroupId(
- AppView<AppInfoWithLiveness> appView,
- String capture,
- DexType iface,
- String pkg,
- String signature,
- DexEncodedMethod mainMethod,
- InnerClassAttribute inner,
- EnclosingMethodAttribute enclosing) {
- super(appView, capture, iface, pkg, signature, mainMethod, inner, enclosing);
- }
-
- @Override
- public boolean equals(Object obj) {
- return obj instanceof GroupId && computeEquals((KotlinLambdaGroupId) obj);
- }
-
- @Override
- String getLambdaKindDescriptor() {
- return "Kotlin k-style lambda group";
- }
-
- @Override
- public LambdaGroup createGroup() {
- return new KStyleLambdaGroup(this);
- }
- }
-
- // Specialized class validator.
- private final class ClassValidator extends KotlinLambdaClassValidator {
- ClassValidator(Kotlin kotlin, AppInfoWithClassHierarchy appInfo) {
- super(kotlin, KStyleLambdaGroup.this, appInfo);
- }
-
- @Override
- int getInstanceInitializerMaxSize(List<DexEncodedField> captures) {
- return captures.size() + 3;
- }
-
- @Override
- int validateInstanceInitializerEpilogue(
- com.android.tools.r8.code.Instruction[] instructions, int index)
- throws LambdaStructureError {
- if (!(instructions[index] instanceof Const4) &&
- !(instructions[index] instanceof Const16)) {
- throw structureError(LAMBDA_INIT_CODE_VERIFICATION_FAILED);
- }
- index++;
- if (!(instructions[index] instanceof com.android.tools.r8.code.InvokeDirect
- || instructions[index] instanceof com.android.tools.r8.code.InvokeDirectRange)
- || instructions[index].getMethod() != kotlin.functional.lambdaInitializerMethod) {
- throw structureError(LAMBDA_INIT_CODE_VERIFICATION_FAILED);
- }
- index++;
- if (!(instructions[index] instanceof ReturnVoid)) {
- throw structureError(LAMBDA_INIT_CODE_VERIFICATION_FAILED);
- }
- return index + 1;
- }
- }
-
- // Specialized class builder.
- private final class ClassBuilder extends KotlinLambdaGroupClassBuilder<KStyleLambdaGroup> {
- ClassBuilder(DexItemFactory factory, InternalOptions options, String origin) {
- super(KStyleLambdaGroup.this, factory, options, origin);
- }
-
- @Override
- protected DexType getSuperClassType() {
- return factory.kotlin.functional.lambdaType;
- }
-
- @Override
- SyntheticSourceCode createInstanceInitializerSourceCode(
- DexType groupClassType, DexMethod initializerMethod, Position callerPosition) {
- return new InstanceInitializerSourceCode(
- factory,
- groupClassType,
- group.getLambdaIdField(factory),
- id -> group.getCaptureField(factory, id),
- initializerMethod,
- factory.kotlin.functional.getArity(id.iface),
- callerPosition);
- }
- }
-
- // Specialized instance initializer code.
- private final static class InstanceInitializerSourceCode
- extends KotlinInstanceInitializerSourceCode {
- private final int arity;
- private final DexMethod lambdaInitializer;
-
- InstanceInitializerSourceCode(
- DexItemFactory factory,
- DexType lambdaGroupType,
- DexField idField,
- IntFunction<DexField> fieldGenerator,
- DexMethod method,
- int arity,
- Position callerPosition) {
- super(lambdaGroupType, idField, fieldGenerator, method, callerPosition);
- this.arity = arity;
- this.lambdaInitializer = factory.createMethod(factory.kotlin.functional.lambdaType,
- factory.createProto(factory.voidType, factory.intType), factory.constructorMethodName);
- }
-
- @Override
- void prepareSuperConstructorCall(int receiverRegister) {
- int arityRegister = nextRegister(ValueType.INT);
- add(builder -> builder.addConst(TypeElement.getInt(), arityRegister, arity));
- add(
- builder ->
- builder.addInvoke(
- Type.DIRECT,
- lambdaInitializer,
- lambdaInitializer.proto,
- Lists.newArrayList(ValueType.OBJECT, ValueType.INT),
- Lists.newArrayList(receiverRegister, arityRegister),
- false /* isInterface */));
- }
- }
-}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/KStyleLambdaGroupIdFactory.java b/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/KStyleLambdaGroupIdFactory.java
deleted file mode 100644
index cc212bf..0000000
--- a/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/KStyleLambdaGroupIdFactory.java
+++ /dev/null
@@ -1,85 +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.ir.optimize.lambda.kotlin;
-
-import com.android.tools.r8.graph.AppView;
-import com.android.tools.r8.graph.ClassAccessFlags;
-import com.android.tools.r8.graph.DexClass;
-import com.android.tools.r8.graph.DexEncodedMethod;
-import com.android.tools.r8.graph.DexType;
-import com.android.tools.r8.graph.InnerClassAttribute;
-import com.android.tools.r8.ir.optimize.lambda.LambdaGroup.LambdaStructureError;
-import com.android.tools.r8.ir.optimize.lambda.LambdaGroupId;
-import com.android.tools.r8.kotlin.Kotlin;
-import com.android.tools.r8.shaking.AppInfoWithLiveness;
-
-public final class KStyleLambdaGroupIdFactory extends KotlinLambdaGroupIdFactory {
- private static final KStyleLambdaGroupIdFactory INSTANCE = new KStyleLambdaGroupIdFactory();
-
- private KStyleLambdaGroupIdFactory() {}
-
- public static KStyleLambdaGroupIdFactory getInstance() {
- return INSTANCE;
- }
-
- @Override
- public LambdaGroupId validateAndCreate(
- AppView<AppInfoWithLiveness> appView, Kotlin kotlin, DexClass lambda)
- throws LambdaStructureError {
- boolean accessRelaxed =
- appView.options().getProguardConfiguration().isAccessModificationAllowed();
-
- // Ignore ACC_SUPER.
- ClassAccessFlags copy = lambda.accessFlags.copy();
- copy.unsetSuper();
- checkAccessFlags("class access flags", copy, PUBLIC_LAMBDA_CLASS_FLAGS, LAMBDA_CLASS_FLAGS);
-
- // Class and interface.
- validateSuperclass(kotlin, lambda);
- DexType iface = validateInterfaces(kotlin, lambda);
-
- validateStaticFields(kotlin, lambda);
- String captureSignature = validateInstanceFields(lambda, accessRelaxed);
- validateDirectMethods(lambda);
- DexEncodedMethod mainMethod = validateVirtualMethods(lambda);
- String genericSignature = validateAnnotations(appView, kotlin, lambda);
- InnerClassAttribute innerClass = validateInnerClasses(lambda);
-
- return new KStyleLambdaGroup.GroupId(
- appView,
- captureSignature,
- iface,
- accessRelaxed ? "" : lambda.type.getPackageDescriptor(),
- genericSignature,
- mainMethod,
- innerClass,
- lambda.getEnclosingMethodAttribute());
- }
-
- @Override
- void validateSuperclass(Kotlin kotlin, DexClass lambda) throws LambdaStructureError {
- if (lambda.superType != kotlin.functional.lambdaType) {
- throw new LambdaStructureError("implements " + lambda.superType.toSourceString() +
- " instead of kotlin.jvm.internal.Lambda");
- }
- }
-
- @Override
- DexType validateInterfaces(Kotlin kotlin, DexClass lambda) throws LambdaStructureError {
- if (lambda.interfaces.size() == 0) {
- throw new LambdaStructureError("does not implement any interfaces");
- }
- if (lambda.interfaces.size() > 1) {
- throw new LambdaStructureError(
- "implements more than one interface: " + lambda.interfaces.size());
- }
- DexType iface = lambda.interfaces.values[0];
- if (!kotlin.functional.isFunctionInterface(iface)) {
- throw new LambdaStructureError("implements " + iface.toSourceString() +
- " instead of kotlin functional interface.");
- }
- return iface;
- }
-}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/KotlinInstanceInitializerSourceCode.java b/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/KotlinInstanceInitializerSourceCode.java
deleted file mode 100644
index 687b081..0000000
--- a/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/KotlinInstanceInitializerSourceCode.java
+++ /dev/null
@@ -1,52 +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.ir.optimize.lambda.kotlin;
-
-import com.android.tools.r8.graph.DexField;
-import com.android.tools.r8.graph.DexMethod;
-import com.android.tools.r8.graph.DexType;
-import com.android.tools.r8.ir.code.Position;
-import com.android.tools.r8.ir.conversion.IRBuilder;
-import com.android.tools.r8.ir.synthetic.SyntheticSourceCode;
-import java.util.function.IntFunction;
-
-abstract class KotlinInstanceInitializerSourceCode extends SyntheticSourceCode {
- private final DexField idField;
- private final IntFunction<DexField> fieldGenerator;
-
- KotlinInstanceInitializerSourceCode(
- DexType lambdaGroupType,
- DexField idField,
- IntFunction<DexField> fieldGenerator,
- DexMethod method,
- Position callerPosition) {
- super(lambdaGroupType, method, callerPosition);
- this.idField = idField;
- this.fieldGenerator = fieldGenerator;
- }
-
- @Override
- protected void prepareInstructions() {
- int receiverRegister = getReceiverRegister();
-
- // Initialize lambda id field.
- add(builder -> builder.addInstancePut(getParamRegister(0), receiverRegister, idField));
-
- // Initialize capture values.
- DexType[] values = proto.parameters.values;
- for (int i = 1; i < values.length; i++) {
- int index = i;
- add(builder -> builder.addInstancePut(
- getParamRegister(index), receiverRegister, fieldGenerator.apply(index - 1)));
- }
-
- // Call superclass constructor.
- prepareSuperConstructorCall(receiverRegister);
-
- add(IRBuilder::addReturn);
- }
-
- abstract void prepareSuperConstructorCall(int receiverRegister);
-}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/KotlinLambdaClassValidator.java b/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/KotlinLambdaClassValidator.java
deleted file mode 100644
index a956f84..0000000
--- a/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/KotlinLambdaClassValidator.java
+++ /dev/null
@@ -1,267 +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.ir.optimize.lambda.kotlin;
-
-import com.android.tools.r8.code.Format22c;
-import com.android.tools.r8.code.Instruction;
-import com.android.tools.r8.code.Iput;
-import com.android.tools.r8.code.IputBoolean;
-import com.android.tools.r8.code.IputByte;
-import com.android.tools.r8.code.IputChar;
-import com.android.tools.r8.code.IputObject;
-import com.android.tools.r8.code.IputShort;
-import com.android.tools.r8.code.IputWide;
-import com.android.tools.r8.code.ReturnVoid;
-import com.android.tools.r8.code.SputObject;
-import com.android.tools.r8.errors.Unreachable;
-import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
-import com.android.tools.r8.graph.Code;
-import com.android.tools.r8.graph.DexClass;
-import com.android.tools.r8.graph.DexEncodedField;
-import com.android.tools.r8.graph.DexEncodedMethod;
-import com.android.tools.r8.graph.DexField;
-import com.android.tools.r8.graph.DexMethod;
-import com.android.tools.r8.graph.DexType;
-import com.android.tools.r8.ir.optimize.Inliner.Reason;
-import com.android.tools.r8.ir.optimize.inliner.NopWhyAreYouNotInliningReporter;
-import com.android.tools.r8.ir.optimize.inliner.WhyAreYouNotInliningReporter;
-import com.android.tools.r8.ir.optimize.lambda.CaptureSignature;
-import com.android.tools.r8.ir.optimize.lambda.LambdaGroup.LambdaStructureError;
-import com.android.tools.r8.kotlin.Kotlin;
-import com.android.tools.r8.utils.ThrowingConsumer;
-import java.util.List;
-
-// Encapsulates the logic of deep-checking of the lambda class assumptions.
-//
-// For k- and j-style lambdas we only check the code of class and instance
-// initializers to ensure that their code performs no unexpected actions:
-//
-// (a) Class initializer is only present for stateless lambdas and does
-// nothing expect instantiating the instance and storing it in
-// static instance field.
-//
-// (b) Instance initializers stores all captured values in proper capture
-// fields and calls the super constructor passing arity to it.
-abstract class KotlinLambdaClassValidator
- implements ThrowingConsumer<DexClass, LambdaStructureError> {
-
- static final String LAMBDA_INIT_CODE_VERIFICATION_FAILED =
- "instance initializer code verification failed";
- private static final String LAMBDA_CLINIT_CODE_VERIFICATION_FAILED =
- "static initializer code verification failed";
-
- final Kotlin kotlin;
- private final KotlinLambdaGroup group;
- private final AppInfoWithClassHierarchy appInfo;
-
- KotlinLambdaClassValidator(
- Kotlin kotlin, KotlinLambdaGroup group, AppInfoWithClassHierarchy appInfo) {
- this.kotlin = kotlin;
- this.group = group;
- this.appInfo = appInfo;
- }
-
- // Report a structure error.
- final LambdaStructureError structureError(String s) {
- return new LambdaStructureError(s, false);
- }
-
- @Override
- public void accept(DexClass lambda) throws LambdaStructureError {
- if (!CaptureSignature.getCaptureSignature(lambda.instanceFields()).equals(group.id().capture)) {
- throw structureError("capture signature was modified");
- }
-
- DexEncodedMethod classInitializer = null;
- DexEncodedMethod instanceInitializer = null;
- for (DexEncodedMethod method : lambda.directMethods()) {
- // We only check bodies of class and instance initializers since we don't expect to
- // see any static or private methods and all virtual methods will be translated into
- // same methods dispatching on lambda id to proper code.
- if (method.isClassInitializer()) {
- Code code = method.getCode();
- if (!(group.isStateless() && group.isSingletonLambda(lambda.type))) {
- throw structureError("static initializer on non-singleton lambda");
- }
- if (classInitializer != null || code == null || !code.isDexCode()) {
- throw structureError(LAMBDA_CLINIT_CODE_VERIFICATION_FAILED);
- }
- validateStatelessLambdaClassInitializer(lambda, code.asDexCode());
- classInitializer = method;
-
- } else if (method.isInstanceInitializer()) {
- Code code = method.getCode();
- if (instanceInitializer != null || code == null || !code.isDexCode()) {
- throw structureError(LAMBDA_INIT_CODE_VERIFICATION_FAILED);
- }
- validateInstanceInitializer(lambda, code.asDexCode());
- instanceInitializer = method;
- }
- }
-
- if (group.isStateless() && group.isSingletonLambda(lambda.type) && (classInitializer == null)) {
- throw structureError("missing static initializer on singleton lambda");
- }
-
- // This check is actually not required for lambda class merging, we only have to do
- // this because group class method composition piggybacks on inlining which has
- // assertions checking when we can inline force-inlined methods. So we double-check
- // these assertion never triggers.
- //
- // NOTE: the real type name for lambda group class is not generated yet, but we
- // can safely use a fake one here.
- DexType fakeLambdaGroupType = kotlin.factory.createType(
- "L" + group.getTypePackage() + "-$$LambdaGroup$XXXX;");
- WhyAreYouNotInliningReporter whyAreYouNotInliningReporter =
- NopWhyAreYouNotInliningReporter.getInstance();
- for (DexEncodedMethod method : lambda.virtualMethods()) {
- if (!method.isInliningCandidate(
- fakeLambdaGroupType, Reason.SIMPLE, appInfo, whyAreYouNotInliningReporter)) {
- throw structureError("method " + method.method.toSourceString() +
- " is not inline-able into lambda group class");
- }
- }
- }
-
- abstract int getInstanceInitializerMaxSize(List<DexEncodedField> captures);
-
- abstract int validateInstanceInitializerEpilogue(
- com.android.tools.r8.code.Instruction[] instructions, int index) throws LambdaStructureError;
-
- private void validateInstanceInitializer(DexClass lambda, Code code)
- throws LambdaStructureError {
- List<DexEncodedField> captures = lambda.instanceFields();
- com.android.tools.r8.code.Instruction[] instructions = code.asDexCode().instructions;
- int index = 0;
-
- if (instructions.length > getInstanceInitializerMaxSize(captures)) {
- throw structureError(LAMBDA_INIT_CODE_VERIFICATION_FAILED);
- }
-
- // Capture field assignments: go through captured fields in assumed order
- // and ensure they are assigned values from appropriate parameters.
- index = validateInstanceInitializerParameterMapping(captures, instructions, index);
-
- // Check the constructor epilogue: a call to superclass constructor.
- index = validateInstanceInitializerEpilogue(instructions, index);
- assert index == instructions.length;
- }
-
- private int validateInstanceInitializerParameterMapping(
- List<DexEncodedField> captures, Instruction[] instructions, int index)
- throws LambdaStructureError {
- int dead = 0;
- int wideFieldsSeen = 0;
- for (DexEncodedField field : captures) {
- if (field.getOptimizationInfo().isDead()) {
- dead++;
- continue;
- }
- switch (field.field.type.toShorty()) {
- case 'Z':
- if (!(instructions[index] instanceof IputBoolean)
- || (instructions[index].getField() != field.field)
- || (((Format22c) instructions[index]).A != (index + 1 + dead + wideFieldsSeen))) {
- throw structureError(LAMBDA_INIT_CODE_VERIFICATION_FAILED);
- }
- break;
-
- case 'B':
- if (!(instructions[index] instanceof IputByte)
- || (instructions[index].getField() != field.field)
- || (((Format22c) instructions[index]).A != (index + 1 + dead + wideFieldsSeen))) {
- throw structureError(LAMBDA_INIT_CODE_VERIFICATION_FAILED);
- }
- break;
-
- case 'S':
- if (!(instructions[index] instanceof IputShort)
- || (instructions[index].getField() != field.field)
- || (((Format22c) instructions[index]).A != (index + 1 + dead + wideFieldsSeen))) {
- throw structureError(LAMBDA_INIT_CODE_VERIFICATION_FAILED);
- }
- break;
-
- case 'C':
- if (!(instructions[index] instanceof IputChar)
- || (instructions[index].getField() != field.field)
- || (((Format22c) instructions[index]).A != (index + 1 + dead + wideFieldsSeen))) {
- throw structureError(LAMBDA_INIT_CODE_VERIFICATION_FAILED);
- }
- break;
-
- case 'I':
- case 'F':
- if (!(instructions[index] instanceof Iput)
- || (instructions[index].getField() != field.field)
- || (((Format22c) instructions[index]).A != (index + 1 + dead + wideFieldsSeen))) {
- throw structureError(LAMBDA_INIT_CODE_VERIFICATION_FAILED);
- }
- break;
-
- case 'J':
- case 'D':
- if (!(instructions[index] instanceof IputWide)
- || (instructions[index].getField() != field.field)
- || (((Format22c) instructions[index]).A != (index + 1 + dead + wideFieldsSeen))) {
- throw structureError(LAMBDA_INIT_CODE_VERIFICATION_FAILED);
- }
- wideFieldsSeen++;
- break;
-
- case 'L':
- if (!(instructions[index] instanceof IputObject)
- || (instructions[index].getField() != field.field)
- || (((Format22c) instructions[index]).A != (index + 1 + dead + wideFieldsSeen))) {
- throw structureError(LAMBDA_INIT_CODE_VERIFICATION_FAILED);
- }
- break;
-
- default:
- throw new Unreachable();
- }
- index++;
- }
- return index;
- }
-
- private void validateStatelessLambdaClassInitializer(DexClass lambda, Code code)
- throws LambdaStructureError {
- assert group.isStateless() && group.isSingletonLambda(lambda.type);
- com.android.tools.r8.code.Instruction[] instructions = code.asDexCode().instructions;
- if (instructions.length != 4) {
- throw structureError(LAMBDA_CLINIT_CODE_VERIFICATION_FAILED);
- }
- if (!(instructions[0] instanceof com.android.tools.r8.code.NewInstance)
- || ((com.android.tools.r8.code.NewInstance) instructions[0]).getType() != lambda.type) {
- throw structureError(LAMBDA_CLINIT_CODE_VERIFICATION_FAILED);
- }
- if (!(instructions[1] instanceof com.android.tools.r8.code.InvokeDirect
- || instructions[1] instanceof com.android.tools.r8.code.InvokeDirectRange)
- || !isLambdaInitializerMethod(lambda, instructions[1].getMethod())) {
- throw structureError(LAMBDA_CLINIT_CODE_VERIFICATION_FAILED);
- }
- if (!(instructions[2] instanceof SputObject)
- || !isLambdaSingletonField(lambda, instructions[2].getField())) {
- throw structureError(LAMBDA_CLINIT_CODE_VERIFICATION_FAILED);
- }
- if (!(instructions[3] instanceof ReturnVoid)) {
- throw structureError(LAMBDA_CLINIT_CODE_VERIFICATION_FAILED);
- }
- }
-
- private boolean isLambdaSingletonField(DexClass lambda, DexField field) {
- return field.type == lambda.type
- && field.holder == lambda.type
- && field.name == kotlin.functional.kotlinStyleLambdaInstanceName;
- }
-
- private boolean isLambdaInitializerMethod(DexClass holder, DexMethod method) {
- return method.holder == holder.type
- && method.name == kotlin.factory.constructorMethodName
- && method.proto.parameters.isEmpty()
- && method.proto.returnType == kotlin.factory.voidType;
- }
-}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/KotlinLambdaConstants.java b/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/KotlinLambdaConstants.java
deleted file mode 100644
index 9ac5ca7..0000000
--- a/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/KotlinLambdaConstants.java
+++ /dev/null
@@ -1,54 +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.ir.optimize.lambda.kotlin;
-
-import com.android.tools.r8.dex.Constants;
-import com.android.tools.r8.graph.ClassAccessFlags;
-import com.android.tools.r8.graph.FieldAccessFlags;
-import com.android.tools.r8.graph.MethodAccessFlags;
-
-interface KotlinLambdaConstants {
- // Default lambda class flags.
- ClassAccessFlags LAMBDA_CLASS_FLAGS =
- ClassAccessFlags.fromDexAccessFlags(Constants.ACC_FINAL);
- // Access-relaxed lambda class flags.
- ClassAccessFlags PUBLIC_LAMBDA_CLASS_FLAGS =
- ClassAccessFlags.fromDexAccessFlags(Constants.ACC_PUBLIC + Constants.ACC_FINAL);
-
- // Default lambda class initializer flags.
- MethodAccessFlags CLASS_INITIALIZER_FLAGS =
- MethodAccessFlags.fromSharedAccessFlags(Constants.ACC_STATIC, true);
- // Default lambda class constructor flags.
- MethodAccessFlags CONSTRUCTOR_FLAGS =
- MethodAccessFlags.fromSharedAccessFlags(0, true);
- // Access-relaxed lambda class constructor flags.
- MethodAccessFlags CONSTRUCTOR_FLAGS_RELAXED =
- MethodAccessFlags.fromSharedAccessFlags(Constants.ACC_PUBLIC, true);
-
- // Default main lambda method flags.
- MethodAccessFlags MAIN_METHOD_FLAGS =
- MethodAccessFlags.fromSharedAccessFlags(
- Constants.ACC_PUBLIC + Constants.ACC_FINAL, false);
- // Default bridge lambda method flags.
- MethodAccessFlags BRIDGE_METHOD_FLAGS =
- MethodAccessFlags.fromSharedAccessFlags(
- Constants.ACC_PUBLIC + Constants.ACC_SYNTHETIC + Constants.ACC_BRIDGE, false);
- // Bridge lambda method flags after inliner.
- MethodAccessFlags BRIDGE_METHOD_FLAGS_FIXED =
- MethodAccessFlags.fromSharedAccessFlags(Constants.ACC_PUBLIC, false);
-
- // Default singleton instance folding field flags.
- FieldAccessFlags SINGLETON_FIELD_FLAGS =
- FieldAccessFlags.fromSharedAccessFlags(
- Constants.ACC_PUBLIC + Constants.ACC_STATIC + Constants.ACC_FINAL);
- // Default instance (lambda capture) field flags.
- FieldAccessFlags CAPTURE_FIELD_FLAGS =
- FieldAccessFlags.fromSharedAccessFlags(
- Constants.ACC_FINAL + Constants.ACC_SYNTHETIC);
- // access-relaxed instance (lambda capture) field flags.
- FieldAccessFlags CAPTURE_FIELD_FLAGS_RELAXED =
- FieldAccessFlags.fromSharedAccessFlags(
- Constants.ACC_PUBLIC + Constants.ACC_FINAL + Constants.ACC_SYNTHETIC);
-}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/KotlinLambdaGroup.java b/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/KotlinLambdaGroup.java
deleted file mode 100644
index fc3f2bd..0000000
--- a/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/KotlinLambdaGroup.java
+++ /dev/null
@@ -1,85 +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.ir.optimize.lambda.kotlin;
-
-import com.android.tools.r8.graph.DexField;
-import com.android.tools.r8.graph.DexItemFactory;
-import com.android.tools.r8.graph.DexProto;
-import com.android.tools.r8.graph.DexType;
-import com.android.tools.r8.ir.optimize.lambda.CaptureSignature;
-import com.android.tools.r8.ir.optimize.lambda.CodeProcessor.Strategy;
-import com.android.tools.r8.ir.optimize.lambda.LambdaGroup;
-import com.android.tools.r8.ir.optimize.lambda.LambdaGroupId;
-
-// Represents a lambda group created to combine several lambda classes generated
-// by kotlin compiler for either regular kotlin lambda expressions (k-style lambdas)
-// or lambda expressions created to implement java SAM interface.
-abstract class KotlinLambdaGroup extends LambdaGroup {
- private final Strategy strategy = new KotlinLambdaGroupCodeStrategy(this);
-
- KotlinLambdaGroup(LambdaGroupId id) {
- super(id);
- }
-
- final KotlinLambdaGroupId id() {
- return (KotlinLambdaGroupId) id;
- }
-
- final boolean isStateless() {
- return id().capture.isEmpty();
- }
-
- final boolean hasAnySingletons() {
- assert isStateless();
- return anyLambda(info -> this.isSingletonLambda(info.clazz.type));
- }
-
- final boolean isSingletonLambda(DexType lambda) {
- assert isStateless();
- return lambdaSingletonField(lambda) != null;
- }
-
- // Field referencing singleton instance for a lambda with specified id.
- final DexField getSingletonInstanceField(DexItemFactory factory, int id) {
- return factory.createField(this.getGroupClassType(),
- this.getGroupClassType(), factory.createString("INSTANCE$" + id));
- }
-
- @Override
- protected String getTypePackage() {
- String pkg = id().pkg;
- return pkg.isEmpty() ? "" : (pkg + "/");
- }
-
- final DexProto createConstructorProto(DexItemFactory factory) {
- String capture = id().capture;
- DexType[] newParameters = new DexType[capture.length() + 1];
- newParameters[0] = factory.intType; // Lambda id.
- for (int i = 0; i < capture.length(); i++) {
- newParameters[i + 1] = CaptureSignature.fieldType(factory, capture, i);
- }
- return factory.createProto(factory.voidType, newParameters);
- }
-
- final DexField getLambdaIdField(DexItemFactory factory) {
- return factory.createField(this.getGroupClassType(), factory.intType, "$id$");
- }
-
- final int mapFieldIntoCaptureIndex(DexType lambda, DexField field) {
- return CaptureSignature.mapFieldIntoCaptureIndex(
- id().capture, lambdaCaptureFields(lambda), field);
- }
-
- final DexField getCaptureField(DexItemFactory factory, int index) {
- assert index >= 0 && index < id().capture.length();
- return factory.createField(this.getGroupClassType(),
- CaptureSignature.fieldType(factory, id().capture, index), "$capture$" + index);
- }
-
- @Override
- public Strategy getCodeStrategy() {
- return strategy;
- }
-}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/KotlinLambdaGroupClassBuilder.java b/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/KotlinLambdaGroupClassBuilder.java
deleted file mode 100644
index 977bdcf..0000000
--- a/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/KotlinLambdaGroupClassBuilder.java
+++ /dev/null
@@ -1,354 +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.ir.optimize.lambda.kotlin;
-
-import static com.android.tools.r8.ir.analysis.type.Nullability.definitelyNotNull;
-
-import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
-import com.android.tools.r8.graph.AppView;
-import com.android.tools.r8.graph.ClassAccessFlags;
-import com.android.tools.r8.graph.Code;
-import com.android.tools.r8.graph.DexAnnotationSet;
-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.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.DexValue.DexValueNull;
-import com.android.tools.r8.graph.EnclosingMethodAttribute;
-import com.android.tools.r8.graph.GenericSignature;
-import com.android.tools.r8.graph.GenericSignature.ClassSignature;
-import com.android.tools.r8.graph.GenericSignature.FieldTypeSignature;
-import com.android.tools.r8.graph.GenericSignature.MethodTypeSignature;
-import com.android.tools.r8.graph.InnerClassAttribute;
-import com.android.tools.r8.graph.MethodAccessFlags;
-import com.android.tools.r8.graph.ParameterAnnotationsList;
-import com.android.tools.r8.ir.analysis.type.ClassTypeElement;
-import com.android.tools.r8.ir.code.IntSwitch;
-import com.android.tools.r8.ir.code.Position;
-import com.android.tools.r8.ir.optimize.info.OptimizationFeedback;
-import com.android.tools.r8.ir.optimize.lambda.LambdaGroupClassBuilder;
-import com.android.tools.r8.ir.synthetic.SynthesizedCode;
-import com.android.tools.r8.ir.synthetic.SyntheticSourceCode;
-import com.android.tools.r8.origin.SynthesizedOrigin;
-import com.android.tools.r8.utils.Box;
-import com.android.tools.r8.utils.IntBox;
-import com.android.tools.r8.utils.InternalOptions;
-import com.android.tools.r8.utils.TriConsumer;
-import com.google.common.collect.Lists;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.LinkedHashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.Map.Entry;
-
-// Builds components of kotlin lambda group class.
-abstract class KotlinLambdaGroupClassBuilder<T extends KotlinLambdaGroup>
- extends LambdaGroupClassBuilder<T> implements KotlinLambdaConstants {
-
- final KotlinLambdaGroupId id;
- final InternalOptions options;
-
- KotlinLambdaGroupClassBuilder(
- T group, DexItemFactory factory, InternalOptions options, String origin) {
- super(group, factory, origin);
- this.id = group.id();
- this.options = options;
- }
-
- abstract SyntheticSourceCode createInstanceInitializerSourceCode(
- DexType groupClassType, DexMethod initializerMethod, Position callerPosition);
-
- // Always generate public final classes.
- @Override
- protected ClassAccessFlags buildAccessFlags() {
- return PUBLIC_LAMBDA_CLASS_FLAGS;
- }
-
- // Take the attribute from the group, if exists.
- @Override
- protected EnclosingMethodAttribute buildEnclosingMethodAttribute() {
- return id.enclosing;
- }
-
- // Take the attribute from the group, if exists.
- @Override
- protected List<InnerClassAttribute> buildInnerClasses() {
- return !id.hasInnerClassAttribute()
- ? Collections.emptyList()
- : Lists.newArrayList(
- new InnerClassAttribute(id.innerClassAccess, group.getGroupClassType(), null, null));
- }
-
- @Override
- protected ClassSignature buildClassSignature() {
- // Kotlin-style lambdas supported by the merged may only contain optional signature and
- // kotlin metadata annotations. We remove the latter, but keep the signature if present.
- return GenericSignature.parseClassSignature(
- origin, id.signature, new SynthesizedOrigin(origin, getClass()), factory, options.reporter);
- }
-
- @Override
- protected DexEncodedMethod[] buildVirtualMethods() {
- // All virtual method are dispatched on $id$ field.
- //
- // For each of the virtual method name/signatures seen in the group
- // we generate a correspondent method in lambda group class with same
- // name/signatures dispatching the call to appropriate code taken
- // from the lambda class.
-
- Map<DexString, Map<DexProto, List<DexEncodedMethod>>> methods = collectVirtualMethods();
- List<DexEncodedMethod> result = new ArrayList<>();
-
- for (Entry<DexString, Map<DexProto, List<DexEncodedMethod>>> upper : methods.entrySet()) {
- DexString methodName = upper.getKey();
- for (Entry<DexProto, List<DexEncodedMethod>> inner : upper.getValue().entrySet()) {
- // Methods for unique name/signature pair.
- DexProto methodProto = inner.getKey();
- List<DexEncodedMethod> implMethods = inner.getValue();
-
- boolean isMainMethod =
- id.mainMethodName == methodName && id.mainMethodProto == methodProto;
-
- // Merging lambdas can introduce methods with too many instructions for the verifier on
- // ART to give up statically verifying the method. We therefore split up the implementation
- // methods and chain them with fallthrough:
- // function <method>() {
- // switch(field.id) {
- // case 1:
- // case 2:
- // ...
- // case n:
- // default: <method$1>()
- // }
- //
- // function <method$1>() {
- // case n + 1:
- // case n + 2:
- // ...
- // case n + m:
- // default: throw null
- // }
- IntBox counter = new IntBox(0);
- Box<DexMethod> currentMethodBox =
- new Box<>(factory.createMethod(group.getGroupClassType(), methodProto, methodName));
- splitIntoGroupsBasedOnInstructionSize(
- implMethods,
- (implMethodsToAdd, methodsSoFar, methodsRemaining) -> {
- assert currentMethodBox.isSet();
- // For bridge methods we still use same PUBLIC FINAL as for the main method,
- // since inlining removes BRIDGE & SYNTHETIC attributes from the bridge methods
- // anyways and our new method is a product of inlining.
- MethodAccessFlags accessFlags = MAIN_METHOD_FLAGS.copy();
- DexMethod method = currentMethodBox.get();
- DexMethod fallthrough =
- methodsRemaining
- ? factory.createMethod(
- group.getGroupClassType(),
- methodProto,
- methodName.toString() + "$" + counter.getAndIncrement())
- : null;
- result.add(
- new DexEncodedMethod(
- method,
- accessFlags,
- MethodTypeSignature.noSignature(),
- isMainMethod ? id.mainMethodAnnotations : DexAnnotationSet.empty(),
- isMainMethod
- ? id.mainMethodParamAnnotations
- : ParameterAnnotationsList.empty(),
- new SynthesizedCode(
- callerPosition ->
- new KotlinLambdaVirtualMethodSourceCode(
- factory,
- group.getGroupClassType(),
- method,
- group.getLambdaIdField(factory),
- implMethodsToAdd,
- fallthrough,
- methodsSoFar,
- callerPosition)),
- true));
- currentMethodBox.set(fallthrough);
- });
- assert !currentMethodBox.isSet();
- }
- }
- return result.toArray(DexEncodedMethod.EMPTY_ARRAY);
- }
-
- private void splitIntoGroupsBasedOnInstructionSize(
- List<DexEncodedMethod> implMethods,
- TriConsumer<List<DexEncodedMethod>, Integer, Boolean> consumer) {
- List<DexEncodedMethod> methods = new ArrayList<>();
- // Upper bound in DEX for reading the field for switching on the group id.
- final int fieldLoadInstructionSize = 10;
- int verificationSizeLimitInBytes = options.verificationSizeLimitInBytes();
- int currentInstructionsSize = fieldLoadInstructionSize;
- int implMethodsCommitted = 0;
- for (DexEncodedMethod implMethod : implMethods) {
- int packedSwitchPayloadSize =
- (int)
- (IntSwitch.basePackedSize(options.getInternalOutputMode())
- + IntSwitch.packedPayloadSize(options.getInternalOutputMode(), methods.size()));
- Code code = implMethod.getCode();
- // We only do lambda merging for DEX. If we started doing lambda merging for CF, we would
- // have to compute a size.
- assert code.isDexCode();
- int codeSize = code.asDexCode().codeSizeInBytes();
- int estimatedMethodSize = currentInstructionsSize + codeSize + packedSwitchPayloadSize;
- if (methods.size() > 0 && estimatedMethodSize > verificationSizeLimitInBytes) {
- consumer.accept(methods, implMethodsCommitted, true);
- currentInstructionsSize = fieldLoadInstructionSize;
- implMethodsCommitted += methods.size();
- methods = new ArrayList<>();
- }
- methods.add(implMethod);
- currentInstructionsSize += codeSize;
- }
- consumer.accept(methods, implMethodsCommitted, false);
- }
-
- // Build a map of virtual methods with unique name/proto pointing to a list of methods
- // from lambda classes implementing appropriate logic. The indices in the list correspond
- // to lambda ids. Note that some of the slots in the lists may be empty, indicating the
- // fact that corresponding lambda does not have a virtual method with this signature.
- private Map<DexString, Map<DexProto, List<DexEncodedMethod>>> collectVirtualMethods() {
- Map<DexString, Map<DexProto, List<DexEncodedMethod>>> methods = new LinkedHashMap<>();
- int size = group.size();
- group.forEachLambda(info -> {
- for (DexEncodedMethod method : info.clazz.virtualMethods()) {
- List<DexEncodedMethod> list = methods
- .computeIfAbsent(method.method.name,
- k -> new LinkedHashMap<>())
- .computeIfAbsent(method.method.proto,
- k -> Lists.newArrayList(Collections.nCopies(size, null)));
- assert list.get(info.id) == null;
- list.set(info.id, method);
- }
- });
- return methods;
- }
-
- @Override
- protected DexEncodedMethod[] buildDirectMethods() {
- // We only build an instance initializer and optional class
- // initializer for stateless lambdas.
-
- boolean needsSingletonInstances = group.isStateless() && group.hasAnySingletons();
- DexType groupClassType = group.getGroupClassType();
-
- DexEncodedMethod[] result = new DexEncodedMethod[needsSingletonInstances ? 2 : 1];
- // Instance initializer mapping parameters into capture fields.
- DexProto initializerProto = group.createConstructorProto(factory);
- DexMethod initializerMethod =
- factory.createMethod(groupClassType, initializerProto, factory.constructorMethodName);
- result[0] =
- new DexEncodedMethod(
- initializerMethod,
- CONSTRUCTOR_FLAGS_RELAXED, // always create access-relaxed constructor.
- MethodTypeSignature.noSignature(),
- DexAnnotationSet.empty(),
- ParameterAnnotationsList.empty(),
- new SynthesizedCode(
- callerPosition ->
- createInstanceInitializerSourceCode(
- groupClassType, initializerMethod, callerPosition)),
- true);
-
- // Static class initializer for stateless lambdas.
- if (needsSingletonInstances) {
- DexMethod method =
- factory.createMethod(
- groupClassType,
- factory.createProto(factory.voidType),
- factory.classConstructorMethodName);
- result[1] =
- new DexEncodedMethod(
- method,
- CLASS_INITIALIZER_FLAGS,
- MethodTypeSignature.noSignature(),
- DexAnnotationSet.empty(),
- ParameterAnnotationsList.empty(),
- new SynthesizedCode(
- callerPosition ->
- new ClassInitializerSourceCode(method, factory, group, callerPosition)),
- true);
- }
-
- return result;
- }
-
- @Override
- protected DexEncodedField[] buildInstanceFields() {
- // Lambda id field plus other fields defined by the capture signature.
- String capture = id.capture;
- int size = capture.length();
- DexEncodedField[] result = new DexEncodedField[1 + size];
-
- result[0] =
- new DexEncodedField(
- group.getLambdaIdField(factory),
- CAPTURE_FIELD_FLAGS_RELAXED,
- FieldTypeSignature.noSignature(),
- DexAnnotationSet.empty(),
- null);
-
- for (int id = 0; id < size; id++) {
- result[id + 1] =
- new DexEncodedField(
- group.getCaptureField(factory, id),
- CAPTURE_FIELD_FLAGS_RELAXED,
- FieldTypeSignature.noSignature(),
- DexAnnotationSet.empty(),
- null);
- }
-
- return result;
- }
-
- @Override
- protected DexEncodedField[] buildStaticFields(
- AppView<? extends AppInfoWithClassHierarchy> appView, OptimizationFeedback feedback) {
- if (!group.isStateless()) {
- return DexEncodedField.EMPTY_ARRAY;
- }
- // One field for each singleton lambda in the group.
- List<DexEncodedField> result = new ArrayList<>(group.size());
- group.forEachLambda(
- info -> {
- if (group.isSingletonLambda(info.clazz.type)) {
- DexField field = group.getSingletonInstanceField(factory, info.id);
- DexEncodedField encodedField =
- new DexEncodedField(
- field,
- SINGLETON_FIELD_FLAGS,
- FieldTypeSignature.noSignature(),
- DexAnnotationSet.empty(),
- DexValueNull.NULL);
- result.add(encodedField);
-
- // Record that the field is definitely not null. It is guaranteed to be assigned in the
- // class initializer of the enclosing class before it is read.
- ClassTypeElement exactType =
- ClassTypeElement.create(field.type, definitelyNotNull(), appView);
- feedback.markFieldHasDynamicLowerBoundType(encodedField, exactType);
- feedback.markFieldHasDynamicUpperBoundType(encodedField, exactType);
- }
- });
- assert result.isEmpty() == !group.hasAnySingletons();
- return result.toArray(DexEncodedField.EMPTY_ARRAY);
- }
-
- @Override
- protected DexTypeList buildInterfaces() {
- return new DexTypeList(new DexType[]{id.iface});
- }
-}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/KotlinLambdaGroupCodeStrategy.java b/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/KotlinLambdaGroupCodeStrategy.java
deleted file mode 100644
index 2c88700..0000000
--- a/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/KotlinLambdaGroupCodeStrategy.java
+++ /dev/null
@@ -1,310 +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.ir.optimize.lambda.kotlin;
-
-import static com.android.tools.r8.ir.analysis.type.Nullability.definitelyNotNull;
-import static com.android.tools.r8.ir.analysis.type.Nullability.maybeNull;
-
-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.DexProto;
-import com.android.tools.r8.graph.DexType;
-import com.android.tools.r8.ir.analysis.type.TypeElement;
-import com.android.tools.r8.ir.code.Argument;
-import com.android.tools.r8.ir.code.CheckCast;
-import com.android.tools.r8.ir.code.ConstNumber;
-import com.android.tools.r8.ir.code.InitClass;
-import com.android.tools.r8.ir.code.InstanceGet;
-import com.android.tools.r8.ir.code.Instruction;
-import com.android.tools.r8.ir.code.InvokeDirect;
-import com.android.tools.r8.ir.code.InvokeMethod;
-import com.android.tools.r8.ir.code.InvokeVirtual;
-import com.android.tools.r8.ir.code.NewInstance;
-import com.android.tools.r8.ir.code.StaticGet;
-import com.android.tools.r8.ir.code.Value;
-import com.android.tools.r8.ir.optimize.lambda.CaptureSignature;
-import com.android.tools.r8.ir.optimize.lambda.CodeProcessor;
-import com.android.tools.r8.ir.optimize.lambda.CodeProcessor.Strategy;
-import com.android.tools.r8.ir.optimize.lambda.LambdaGroup;
-import com.android.tools.r8.ir.optimize.lambda.LambdaMerger.ApplyStrategy;
-import java.util.ArrayList;
-import java.util.List;
-
-// Defines the code processing strategy for kotlin lambdas.
-final class KotlinLambdaGroupCodeStrategy implements Strategy {
- private final KotlinLambdaGroup group;
-
- KotlinLambdaGroupCodeStrategy(KotlinLambdaGroup group) {
- this.group = group;
- }
-
- @Override
- public LambdaGroup group() {
- return group;
- }
-
- @Override
- public boolean isValidStaticFieldWrite(CodeProcessor context, DexField field) {
- DexType lambda = field.holder;
- assert group.containsLambda(lambda);
- // Only support writes to singleton static field named 'INSTANCE' from lambda
- // static class initializer.
- return field.name == context.kotlin.functional.kotlinStyleLambdaInstanceName
- && lambda == field.type
- && context.method.getDefinition().isClassInitializer()
- && context.method.getHolderType() == lambda;
- }
-
- @Override
- public boolean isValidStaticFieldRead(CodeProcessor context, DexField field) {
- DexType lambda = field.holder;
- assert group.containsLambda(lambda);
- // Support all reads of singleton static field named 'INSTANCE'.
- return field.name == context.kotlin.functional.kotlinStyleLambdaInstanceName
- && lambda == field.type;
- }
-
- @Override
- public boolean isValidInstanceFieldWrite(CodeProcessor context, DexField field) {
- DexType lambda = field.holder;
- DexMethod method = context.method.getReference();
- assert group.containsLambda(lambda);
- // Support writes to capture instance fields inside lambda constructor only.
- return method.holder == lambda && context.method.getDefinition().isInstanceInitializer();
- }
-
- @Override
- public boolean isValidInstanceFieldRead(CodeProcessor context, DexField field) {
- assert group.containsLambda(field.holder);
- // Support all reads from capture instance fields.
- return true;
- }
-
- @Override
- public boolean isValidNewInstance(CodeProcessor context, NewInstance invoke) {
- // Only valid for stateful lambdas.
- return !(group.isStateless() && group.isSingletonLambda(invoke.clazz));
- }
-
- @Override
- public boolean isValidInvoke(CodeProcessor context, InvokeMethod invoke) {
- return isValidInitializerCall(context, invoke) || isValidVirtualCall(invoke);
- }
-
- private boolean isValidInitializerCall(CodeProcessor context, InvokeMethod invoke) {
- DexMethod method = invoke.getInvokedMethod();
- DexType lambda = method.holder;
- assert group.containsLambda(lambda);
- // Allow calls to a constructor from other classes if the lambda is singleton,
- // otherwise allow such a call only from the same class static initializer.
- boolean isSingletonLambda = group.isStateless() && group.isSingletonLambda(lambda);
- return (isSingletonLambda == (context.method.getHolderType() == lambda))
- && invoke.isInvokeDirect()
- && context.factory.isConstructor(method)
- && CaptureSignature.getCaptureSignature(method.proto.parameters).equals(group.id().capture);
- }
-
- private boolean isValidVirtualCall(InvokeMethod invoke) {
- assert group.containsLambda(invoke.getInvokedMethod().holder);
- // Allow all virtual calls.
- return invoke.isInvokeVirtual();
- }
-
- @Override
- public boolean isValidInitClass(CodeProcessor context, DexType clazz) {
- assert group.containsLambda(clazz);
- // Support all init class instructions.
- return true;
- }
-
- @Override
- public boolean isValidHolder(CodeProcessor context, DexType holder) {
- assert group.containsLambda(holder);
- return true;
- }
-
- @Override
- public void patch(ApplyStrategy context, NewInstance newInstance) {
- DexType oldType = newInstance.clazz;
- DexType newType = group.getGroupClassType();
-
- NewInstance patchedNewInstance =
- new NewInstance(
- newType,
- context.code.createValue(
- TypeElement.fromDexType(newType, definitelyNotNull(), context.appView)));
- context.instructions().replaceCurrentInstruction(patchedNewInstance);
-
- assert newType != oldType;
- context.recordTypeHasChanged(patchedNewInstance.outValue());
- }
-
- @Override
- public void patch(ApplyStrategy context, InvokeMethod invoke) {
- assert group.containsLambda(invoke.getInvokedMethod().holder);
- if (isValidInitializerCall(context, invoke)) {
- patchInitializer(context, invoke.asInvokeDirect());
- } else {
- // Regular calls to virtual methods only need target method be replaced.
- assert isValidVirtualCall(invoke);
- DexMethod oldMethod = invoke.getInvokedMethod();
- DexMethod newMethod = mapVirtualMethod(context.factory, oldMethod);
-
- InvokeVirtual patchedInvokeVirtual =
- new InvokeVirtual(
- newMethod,
- createValueForType(context, newMethod.proto.returnType),
- invoke.arguments());
- context.instructions().replaceCurrentInstruction(patchedInvokeVirtual);
-
- // Otherwise, we need to record that the type of the out-value has changed.
- assert newMethod.proto.returnType == oldMethod.proto.returnType;
- }
- }
-
- @Override
- public void patch(ApplyStrategy context, InstanceGet instanceGet) {
- DexField oldField = instanceGet.getField();
- DexField newField = mapCaptureField(context.factory, oldField.holder, oldField);
-
- DexType oldFieldType = oldField.type;
- DexType newFieldType = newField.type;
-
- // We need to insert remapped values and in case the capture field
- // of type Object optionally cast to expected field.
- InstanceGet newInstanceGet =
- new InstanceGet(createValueForType(context, newFieldType), instanceGet.object(), newField);
- context.instructions().replaceCurrentInstruction(newInstanceGet);
-
- if (oldFieldType.isPrimitiveType() || oldFieldType == context.factory.objectType) {
- return;
- }
-
- // Since all captured values of non-primitive types are stored in fields of type
- // java.lang.Object, we need to cast them to appropriate type to satisfy the verifier.
- TypeElement castTypeLattice =
- TypeElement.fromDexType(oldFieldType, maybeNull(), context.appView);
- Value newValue = context.code.createValue(castTypeLattice, newInstanceGet.getLocalInfo());
- newInstanceGet.outValue().replaceUsers(newValue);
- CheckCast cast = new CheckCast(newValue, newInstanceGet.outValue(), oldFieldType);
- cast.setPosition(newInstanceGet.getPosition());
- context.instructions().add(cast);
-
- // If the current block has catch handlers split the check cast into its own block.
- // Since new cast is never supposed to fail, we leave catch handlers empty.
- if (cast.getBlock().hasCatchHandlers()) {
- context.instructions().previous();
- context.instructions().split(context.code, 1, context.blocks);
- }
- }
-
- @Override
- public void patch(ApplyStrategy context, StaticGet staticGet) {
- DexField oldField = staticGet.getField();
- DexField newField = mapSingletonInstanceField(context.factory, oldField);
-
- StaticGet patchedStaticGet =
- new StaticGet(
- context.code.createValue(
- TypeElement.fromDexType(newField.type, maybeNull(), context.appView)),
- newField);
- context.instructions().replaceCurrentInstruction(patchedStaticGet);
-
- assert newField.type != oldField.type;
- context.recordTypeHasChanged(patchedStaticGet.outValue());
- }
-
- @Override
- public void patch(ApplyStrategy context, InitClass initClass) {
- InitClass pachedInitClass =
- new InitClass(context.code.createValue(TypeElement.getInt()), group.getGroupClassType());
- context.instructions().replaceCurrentInstruction(pachedInitClass);
- }
-
- @Override
- public void patch(ApplyStrategy context, Argument argument) {
- // An argument can be a direct operand to a phi that we potentially could not remove.
- assert argument.getIndex() == 0;
- // The argument value will be replaced by the invoke value.
- argument
- .outValue()
- .setType(TypeElement.fromDexType(group.getGroupClassType(), maybeNull(), context.appView));
- context.recordTypeHasChanged(argument.outValue());
- }
-
- private void patchInitializer(CodeProcessor context, InvokeDirect invoke) {
- // Patching includes:
- // - change of methods
- // - adding lambda id as the first argument
- // - reshuffling other arguments (representing captured values)
- // according to capture signature of the group.
-
- DexMethod method = invoke.getInvokedMethod();
- DexType lambda = method.holder;
-
- // Create constant with lambda id.
- Value lambdaIdValue = context.code.createValue(TypeElement.getInt());
- ConstNumber lambdaId = new ConstNumber(lambdaIdValue, group.lambdaId(lambda));
- lambdaId.setPosition(invoke.getPosition());
- context.instructions().previous();
- context.instructions().add(lambdaId);
-
- // Create a new InvokeDirect instruction.
- Instruction next = context.instructions().next();
- assert next == invoke;
-
- DexMethod newTarget = mapInitializerMethod(context.factory, method);
- List<Value> newArguments = mapInitializerArgs(lambdaIdValue, invoke.arguments(), method.proto);
- context.instructions().replaceCurrentInstruction(
- new InvokeDirect(newTarget, null /* no return value */, newArguments)
- );
- }
-
- private Value createValueForType(CodeProcessor context, DexType returnType) {
- return returnType == context.factory.voidType
- ? null
- : context.code.createValue(
- TypeElement.fromDexType(returnType, maybeNull(), context.appView));
- }
-
- private List<Value> mapInitializerArgs(
- Value lambdaIdValue, List<Value> oldArguments, DexProto proto) {
- assert oldArguments.size() == proto.parameters.size() + 1;
- List<Value> newArguments = new ArrayList<>();
- newArguments.add(oldArguments.get(0)); // receiver
- newArguments.add(lambdaIdValue); // lambda-id
- List<Integer> reverseMapping =
- CaptureSignature.getReverseCaptureMapping(proto.parameters.values);
- for (int index : reverseMapping) {
- // <original-capture-index> = mapping[<normalized-capture-index>]
- newArguments.add(oldArguments.get(index + 1 /* after receiver */));
- }
- return newArguments;
- }
-
- // Map lambda class initializer into lambda group class initializer.
- private DexMethod mapInitializerMethod(DexItemFactory factory, DexMethod method) {
- assert factory.isConstructor(method);
- assert CaptureSignature.getCaptureSignature(method.proto.parameters).equals(group.id().capture);
- return factory.createMethod(group.getGroupClassType(),
- group.createConstructorProto(factory), method.name);
- }
-
- // Map lambda class virtual method into lambda group class method.
- private DexMethod mapVirtualMethod(DexItemFactory factory, DexMethod method) {
- return factory.createMethod(group.getGroupClassType(), method.proto, method.name);
- }
-
- // Map lambda class capture field into lambda group class capture field.
- private DexField mapCaptureField(DexItemFactory factory, DexType lambda, DexField field) {
- return group.getCaptureField(factory, group.mapFieldIntoCaptureIndex(lambda, field));
- }
-
- // Map lambda class initializer into lambda group class initializer.
- private DexField mapSingletonInstanceField(DexItemFactory factory, DexField field) {
- return group.getSingletonInstanceField(factory, group.lambdaId(field.holder));
- }
-}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/KotlinLambdaGroupId.java b/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/KotlinLambdaGroupId.java
deleted file mode 100644
index 7f24d79..0000000
--- a/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/KotlinLambdaGroupId.java
+++ /dev/null
@@ -1,149 +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.ir.optimize.lambda.kotlin;
-
-import com.android.tools.r8.graph.AppView;
-import com.android.tools.r8.graph.DexAnnotationSet;
-import com.android.tools.r8.graph.DexEncodedMethod;
-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.EnclosingMethodAttribute;
-import com.android.tools.r8.graph.InnerClassAttribute;
-import com.android.tools.r8.graph.ParameterAnnotationsList;
-import com.android.tools.r8.ir.optimize.lambda.LambdaGroup;
-import com.android.tools.r8.ir.optimize.lambda.LambdaGroupId;
-import com.android.tools.r8.shaking.AppInfoWithLiveness;
-
-abstract class KotlinLambdaGroupId implements LambdaGroupId {
- private static final int MISSING_INNER_CLASS_ATTRIBUTE = -1;
-
- private final int hash;
-
- // Capture signature.
- final String capture;
- // Kotlin functional interface.
- final DexType iface;
- // Package (in case access relaxation is enabled root package is used).
- final String pkg;
- // Generic signature of the lambda class.
- final String signature;
-
- // Characteristics of the main lambda method. Kotlin generates one main method with
- // the signature matching the lambda signature, and a bridge method (if needed)
- // forwarding the call via interface to the main method. Main method may have
- // generic signature and parameter annotations defining nullability.
- //
- // TODO: address cases when main method is removed after inlining.
- // (the main method if created is supposed to be inlined, since it is always
- // only called from the bridge, and removed. In this case the method with
- // signature and annotations is removed allowing more lambda to be merged.)
- final DexString mainMethodName;
- final DexProto mainMethodProto;
- final DexAnnotationSet mainMethodAnnotations;
- final ParameterAnnotationsList mainMethodParamAnnotations;
-
- final EnclosingMethodAttribute enclosing;
-
- // Note that lambda classes are always created as _anonymous_ inner classes. We only
- // need to store the fact that the class had this attribute and if it had store the
- // access from InnerClassAttribute.
- final int innerClassAccess;
-
- KotlinLambdaGroupId(
- AppView<AppInfoWithLiveness> appView,
- String capture,
- DexType iface,
- String pkg,
- String signature,
- DexEncodedMethod mainMethod,
- InnerClassAttribute inner,
- EnclosingMethodAttribute enclosing) {
- assert capture != null && iface != null && pkg != null && mainMethod != null;
- assert inner == null || (inner.isAnonymous() && inner.getOuter() == null);
- this.capture = capture;
- this.iface = iface;
- this.pkg = pkg;
- this.signature = signature;
- this.mainMethodName = mainMethod.method.name;
- this.mainMethodProto = mainMethod.method.proto;
- this.mainMethodAnnotations = mainMethod.liveAnnotations(appView);
- this.mainMethodParamAnnotations = mainMethod.liveParameterAnnotations(appView);
- this.innerClassAccess = inner != null ? inner.getAccess() : MISSING_INNER_CLASS_ATTRIBUTE;
- this.enclosing = enclosing;
- this.hash = computeHashCode();
- }
-
- final boolean hasInnerClassAttribute() {
- return innerClassAccess != MISSING_INNER_CLASS_ATTRIBUTE;
- }
-
- @Override
- public final int hashCode() {
- return hash;
- }
-
- private int computeHashCode() {
- int hash = capture.hashCode() * 7;
- hash += iface.hashCode() * 17;
- hash += pkg.hashCode() * 37;
- hash += signature != null ? signature.hashCode() * 47 : 0;
- hash += mainMethodName.hashCode() * 71;
- hash += mainMethodProto.hashCode() * 89;
- hash += mainMethodAnnotations != null ? mainMethodAnnotations.hashCode() * 101 : 0;
- hash += mainMethodParamAnnotations != null ? mainMethodParamAnnotations.hashCode() * 113 : 0;
- hash += innerClassAccess * 131;
- hash += enclosing != null ? enclosing.hashCode() * 211 : 0;
- return hash;
- }
-
- @Override
- public abstract boolean equals(Object obj);
-
- boolean computeEquals(KotlinLambdaGroupId other) {
- return capture.equals(other.capture) &&
- iface == other.iface &&
- pkg.equals(other.pkg) &&
- mainMethodName == other.mainMethodName &&
- mainMethodProto == other.mainMethodProto &&
- (mainMethodAnnotations == null ? other.mainMethodAnnotations == null
- : mainMethodAnnotations.equals(other.mainMethodAnnotations)) &&
- (mainMethodParamAnnotations == null ? other.mainMethodParamAnnotations == null
- : mainMethodParamAnnotations.equals(other.mainMethodParamAnnotations)) &&
- (signature == null ? other.signature == null : signature.equals(other.signature)) &&
- innerClassAccess == other.innerClassAccess &&
- (enclosing == null ? other.enclosing == null : enclosing.equals(other.enclosing));
- }
-
- @Override
- public String toString() {
- StringBuilder builder = new StringBuilder(getLambdaKindDescriptor())
- .append("\n capture: ").append(capture)
- .append("\n interface: ").append(iface.descriptor)
- .append("\n package: ").append(pkg)
- .append("\n signature: ").append(signature)
- .append("\n main method name: ").append(mainMethodName.toString())
- .append("\n main method: ").append(mainMethodProto.toSourceString())
- .append("\n main annotations: ").append(mainMethodAnnotations)
- .append("\n main param annotations: ").append(mainMethodParamAnnotations)
- .append("\n inner: ")
- .append(innerClassAccess == MISSING_INNER_CLASS_ATTRIBUTE ? "none" : innerClassAccess);
- if (enclosing != null) {
- if (enclosing.getEnclosingClass() != null) {
- builder.append("\n enclosingClass: ")
- .append(enclosing.getEnclosingClass().descriptor);
- } else {
- builder.append("\n enclosingMethod: ")
- .append(enclosing.getEnclosingMethod().toSourceString());
- }
- }
- return builder.toString();
- }
-
- abstract String getLambdaKindDescriptor();
-
- @Override
- public abstract LambdaGroup createGroup();
-}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/KotlinLambdaGroupIdFactory.java b/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/KotlinLambdaGroupIdFactory.java
deleted file mode 100644
index 472d861..0000000
--- a/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/KotlinLambdaGroupIdFactory.java
+++ /dev/null
@@ -1,244 +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.ir.optimize.lambda.kotlin;
-
-import com.android.tools.r8.graph.AccessFlags;
-import com.android.tools.r8.graph.AppView;
-import com.android.tools.r8.graph.DexAnnotation;
-import com.android.tools.r8.graph.DexClass;
-import com.android.tools.r8.graph.DexEncodedField;
-import com.android.tools.r8.graph.DexEncodedMethod;
-import com.android.tools.r8.graph.DexProgramClass;
-import com.android.tools.r8.graph.DexType;
-import com.android.tools.r8.graph.InnerClassAttribute;
-import com.android.tools.r8.ir.optimize.lambda.CaptureSignature;
-import com.android.tools.r8.ir.optimize.lambda.LambdaGroup.LambdaStructureError;
-import com.android.tools.r8.ir.optimize.lambda.LambdaGroupId;
-import com.android.tools.r8.kotlin.Kotlin;
-import com.android.tools.r8.shaking.AppInfoWithLiveness;
-import java.util.List;
-
-public abstract class KotlinLambdaGroupIdFactory implements KotlinLambdaConstants {
- KotlinLambdaGroupIdFactory() {
- }
-
- public static KotlinLambdaGroupIdFactory getFactoryForClass(DexProgramClass clazz) {
- if (clazz.getKotlinInfo().isSyntheticClass()
- && clazz.getKotlinInfo().asSyntheticClass().isLambda()) {
- if (clazz.getKotlinInfo().asSyntheticClass().isKotlinStyleLambda()) {
- return KStyleLambdaGroupIdFactory.getInstance();
- }
- assert clazz.getKotlinInfo().asSyntheticClass().isJavaStyleLambda();
- return JStyleLambdaGroupIdFactory.getInstance();
- }
- return null;
- }
-
- // Creates a lambda group id for a Java or Kotlin style lambda. Never returns null, but may throw
- // a LambdaStructureError if the lambda does not pass pre-requirements (mostly by not meeting
- // high-level structure expectations).
- //
- // At this point we only perform high-level checks before qualifying the lambda as a candidate
- // for merging and assigning lambda group id. We can NOT perform checks on method bodies since
- // they may not be converted yet, we'll do that in KStyleLambdaClassValidator.
- public abstract LambdaGroupId validateAndCreate(
- AppView<AppInfoWithLiveness> appView, Kotlin kotlin, DexClass lambda)
- throws LambdaStructureError;
-
- abstract void validateSuperclass(Kotlin kotlin, DexClass lambda) throws LambdaStructureError;
-
- abstract DexType validateInterfaces(Kotlin kotlin, DexClass lambda) throws LambdaStructureError;
-
- DexEncodedMethod validateVirtualMethods(DexClass lambda) throws LambdaStructureError {
- DexEncodedMethod mainMethod = null;
-
- for (DexEncodedMethod method : lambda.virtualMethods()) {
- if (method.accessFlags.materialize() == MAIN_METHOD_FLAGS.materialize()) {
- if (mainMethod != null) {
- throw new LambdaStructureError("more than one main method found");
- }
- mainMethod = method;
- } else {
- checkAccessFlags("unexpected virtual method access flags",
- method.accessFlags, BRIDGE_METHOD_FLAGS, BRIDGE_METHOD_FLAGS_FIXED);
- checkDirectMethodAnnotations(method);
- }
- }
-
- if (mainMethod == null) {
- // Missing main method may be a result of tree shaking.
- throw new LambdaStructureError("no main method found", false);
- }
- return mainMethod;
- }
-
- InnerClassAttribute validateInnerClasses(DexClass lambda) throws LambdaStructureError {
- List<InnerClassAttribute> innerClasses = lambda.getInnerClasses();
- if (innerClasses != null) {
- for (InnerClassAttribute inner : innerClasses) {
- if (inner.getInner() == lambda.type) {
- if (!inner.isAnonymous()) {
- throw new LambdaStructureError("is not anonymous");
- }
- return inner;
- }
- }
- }
- return null;
- }
-
- public static boolean hasValidAnnotations(Kotlin kotlin, DexClass lambda) {
- for (DexAnnotation annotation : lambda.annotations().annotations) {
- if (annotation.annotation.type == kotlin.factory.kotlinMetadataType) {
- continue;
- }
- return false;
- }
- return true;
- }
-
- String validateAnnotations(AppView<AppInfoWithLiveness> appView, Kotlin kotlin, DexClass lambda)
- throws LambdaStructureError {
- for (DexAnnotation annotation : lambda.liveAnnotations(appView).annotations) {
- if (annotation.annotation.type == appView.dexItemFactory().kotlinMetadataType) {
- // Ignore kotlin metadata on lambda classes. Metadata on synthetic
- // classes exists but is not used in the current Kotlin version (1.2.21)
- // and newly generated lambda _group_ class is not exactly a kotlin class.
- continue;
- }
-
- assert !hasValidAnnotations(kotlin, lambda);
- throw new LambdaStructureError(
- "unexpected annotation: " + annotation.annotation.type.toSourceString());
- }
- assert hasValidAnnotations(kotlin, lambda);
- return lambda.getClassSignature().toString();
- }
-
- void validateStaticFields(Kotlin kotlin, DexClass lambda) throws LambdaStructureError {
- List<DexEncodedField> staticFields = lambda.staticFields();
- if (staticFields.size() == 1) {
- DexEncodedField field = staticFields.get(0);
- if (field.field.name != kotlin.functional.kotlinStyleLambdaInstanceName ||
- field.field.type != lambda.type || !field.accessFlags.isPublic() ||
- !field.accessFlags.isFinal() || !field.accessFlags.isStatic()) {
- throw new LambdaStructureError("unexpected static field " + field.toSourceString());
- }
- // No state if the lambda is a singleton.
- if (lambda.instanceFields().size() > 0) {
- throw new LambdaStructureError("has instance fields along with INSTANCE");
- }
- checkAccessFlags("static field access flags", field.accessFlags, SINGLETON_FIELD_FLAGS);
- checkFieldAnnotations(field);
-
- } else if (staticFields.size() > 1) {
- throw new LambdaStructureError(
- "only one static field max expected, found " + staticFields.size());
- }
- }
-
- String validateInstanceFields(DexClass lambda, boolean accessRelaxed)
- throws LambdaStructureError {
- List<DexEncodedField> instanceFields = lambda.instanceFields();
- for (DexEncodedField field : instanceFields) {
- checkAccessFlags("capture field access flags", field.accessFlags,
- accessRelaxed ? CAPTURE_FIELD_FLAGS_RELAXED : CAPTURE_FIELD_FLAGS);
- checkFieldAnnotations(field);
- }
- return CaptureSignature.getCaptureSignature(instanceFields);
- }
-
- void validateDirectMethods(DexClass lambda) throws LambdaStructureError {
- for (DexEncodedMethod method : lambda.directMethods()) {
- if (method.isClassInitializer()) {
- // We expect to see class initializer only if there is a singleton field.
- if (lambda.staticFields().size() != 1) {
- throw new LambdaStructureError("has static initializer, but no singleton field");
- }
- checkAccessFlags(
- "unexpected static initializer access flags",
- method.accessFlags.getOriginalAccessFlags(),
- CLASS_INITIALIZER_FLAGS);
- checkDirectMethodAnnotations(method);
- } else if (method.isStatic()) {
- throw new LambdaStructureError(
- "unexpected static method: " + method.method.toSourceString());
- } else if (method.isInstanceInitializer()) {
- // Lambda class is expected to have one constructor
- // with parameters matching capture signature.
- DexType[] parameters = method.method.proto.parameters.values;
- List<DexEncodedField> instanceFields = lambda.instanceFields();
- if (parameters.length != instanceFields.size()) {
- throw new LambdaStructureError("constructor parameters don't match captured values.");
- }
- for (int i = 0; i < parameters.length; i++) {
- // Kotlin compiler sometimes reshuffles the parameters so that their order
- // in the constructor don't match order of capture fields. We could add
- // support for it, but it happens quite rarely so don't bother for now.
- if (parameters[i] != instanceFields.get(i).field.type) {
- throw new LambdaStructureError(
- "constructor parameters don't match captured values.", false);
- }
- }
- checkAccessFlags("unexpected constructor access flags",
- method.accessFlags, CONSTRUCTOR_FLAGS, CONSTRUCTOR_FLAGS_RELAXED);
- checkDirectMethodAnnotations(method);
-
- } else if (method.isPrivateMethod()) {
- // TODO(b/135975229)
- throw new LambdaStructureError("private method: " + method.method.toSourceString());
- } else {
- assert false;
- throw new LambdaStructureError(
- "unexpected method encountered: " + method.method.toSourceString());
- }
- }
- }
-
- private static void checkDirectMethodAnnotations(DexEncodedMethod method)
- throws LambdaStructureError {
- if (!method.annotations().isEmpty()) {
- throw new LambdaStructureError(
- "unexpected method annotations ["
- + method.annotations().toSmaliString()
- + "] on "
- + method.method.toSourceString());
- }
- if (!method.parameterAnnotationsList.isEmpty()) {
- throw new LambdaStructureError(
- "unexpected method parameters annotations ["
- + method.parameterAnnotationsList.toSmaliString()
- + "] on "
- + method.method.toSourceString());
- }
- }
-
- private static void checkFieldAnnotations(DexEncodedField field) throws LambdaStructureError {
- if (field.hasAnnotation()) {
- throw new LambdaStructureError(
- "unexpected field annotations ["
- + field.annotations().toSmaliString()
- + "] on "
- + field.field.toSourceString());
- }
- }
-
- @SafeVarargs
- static <T extends AccessFlags> void checkAccessFlags(
- String message, T actual, T... expected) throws LambdaStructureError {
- checkAccessFlags(message, actual.materialize(), expected);
- }
-
- @SafeVarargs
- private static <T extends AccessFlags> void checkAccessFlags(
- String message, int actual, T... expected) throws LambdaStructureError {
- for (T flag : expected) {
- if (actual == flag.materialize()) {
- return;
- }
- }
- throw new LambdaStructureError(message);
- }
-}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/KotlinLambdaVirtualMethodSourceCode.java b/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/KotlinLambdaVirtualMethodSourceCode.java
deleted file mode 100644
index 657a796..0000000
--- a/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/KotlinLambdaVirtualMethodSourceCode.java
+++ /dev/null
@@ -1,122 +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.ir.optimize.lambda.kotlin;
-
-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.DexType;
-import com.android.tools.r8.ir.code.Invoke;
-import com.android.tools.r8.ir.code.Position;
-import com.android.tools.r8.ir.code.Value;
-import com.android.tools.r8.ir.code.ValueType;
-import com.android.tools.r8.ir.conversion.IRBuilder;
-import com.android.tools.r8.ir.synthetic.SyntheticSourceCode;
-import java.util.ArrayList;
-import java.util.List;
-
-final class KotlinLambdaVirtualMethodSourceCode extends SyntheticSourceCode {
- private final DexItemFactory factory;
- private final DexField idField;
- private final List<DexEncodedMethod> implMethods;
- private final DexMethod fallThroughMethod;
- private final int keyStart;
-
- KotlinLambdaVirtualMethodSourceCode(
- DexItemFactory factory,
- DexType groupClass,
- DexMethod method,
- DexField idField,
- List<DexEncodedMethod> implMethods,
- DexMethod fallThroughMethod,
- int keyStart,
- Position callerPosition) {
- super(groupClass, method, callerPosition);
- this.factory = factory;
- this.idField = idField;
- this.implMethods = implMethods;
- this.fallThroughMethod = fallThroughMethod;
- this.keyStart = keyStart;
- }
-
- @Override
- protected void prepareInstructions() {
- int implMethodCount = implMethods.size();
- // We generate a single switch on lambda $id value read from appropriate
- // field, and for each lambda id generate a call to appropriate method of
- // the lambda class. Since this methods are marked as 'force inline',
- // they are inlined by the inliner.
-
- // Return value register if needed.
- DexType returnType = proto.returnType;
- boolean returnsValue = returnType != factory.voidType;
- ValueType retValueType = returnsValue ? ValueType.fromDexType(returnType) : null;
- int retRegister = returnsValue ? nextRegister(retValueType) : -1;
-
- // Lambda id register to switch on.
- int idRegister = nextRegister(ValueType.INT);
- add(builder -> builder.addInstanceGet(idRegister, getReceiverRegister(), idField));
-
- // Switch on id.
- // Note that 'keys' and 'offsets' are just captured here and filled
- // in with values when appropriate basic blocks are created.
- int[] keys = new int[implMethodCount];
- int[] offsets = new int[implMethodCount];
- int[] fallthrough = new int[1]; // Array as a container for late initialization.
- int switchIndex = lastInstructionIndex();
- add(builder -> builder.addSwitch(idRegister, keys, fallthrough[0], offsets),
- builder -> endsSwitch(builder, switchIndex, fallthrough[0], offsets));
-
- List<Value> arguments = new ArrayList<>(proto.parameters.values.length + 1);
-
- fallthrough[0] = nextInstructionIndex();
- if (fallThroughMethod == null) {
- // Fallthrough treated as unreachable.
- int nullRegister = nextRegister(ValueType.OBJECT);
- add(builder -> builder.addNullConst(nullRegister));
- add(builder -> builder.addThrow(nullRegister), endsBlock);
- } else {
- addMethodCall(fallThroughMethod, arguments, returnsValue, retRegister);
- }
-
- // Blocks for each lambda id.
- for (int i = 0; i < implMethodCount; i++) {
- keys[i] = keyStart + i;
- DexEncodedMethod impl = implMethods.get(i);
- if (impl == null) {
- // Virtual method is missing in lambda class.
- offsets[i] = fallthrough[0];
- continue;
- }
- offsets[i] = nextInstructionIndex();
- addMethodCall(impl.method, arguments, returnsValue, retRegister);
- }
- }
-
- private void addMethodCall(
- DexMethod method, List<Value> arguments, boolean returnsValue, int retRegister) {
- // Emit fake call on `this` receiver.
- add(
- builder -> {
- if (arguments.isEmpty()) {
- arguments.add(builder.getReceiverValue());
- List<Value> argumentValues = builder.getArgumentValues();
- if (argumentValues != null) {
- arguments.addAll(builder.getArgumentValues());
- }
- }
- builder.addInvoke(
- Invoke.Type.VIRTUAL, method, method.proto, arguments, false /* isInterface */);
- });
- // Handle return value if needed.
- if (returnsValue) {
- add(builder -> builder.addMoveResult(retRegister));
- add(builder -> builder.addReturn(retRegister), endsBlock);
- } else {
- add(IRBuilder::addReturn, endsBlock);
- }
- }
-}
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 6b011a6..6fc09f5 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinClassMetadataReader.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinClassMetadataReader.java
@@ -7,6 +7,7 @@
import static com.android.tools.r8.kotlin.KotlinMetadataUtils.NO_KOTLIN_INFO;
import static com.android.tools.r8.kotlin.KotlinSyntheticClassInfo.getFlavour;
+import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexAnnotation;
import com.android.tools.r8.graph.DexAnnotationElement;
import com.android.tools.r8.graph.DexClass;
@@ -35,11 +36,10 @@
DexClass clazz,
DexItemFactory factory,
Reporter reporter,
- boolean onlyProcessLambda,
Consumer<DexEncodedMethod> keepByteCode) {
DexAnnotation meta = clazz.annotations().getFirstMatching(factory.kotlinMetadataType);
if (meta != null) {
- return getKotlinInfo(kotlin, clazz, factory, reporter, onlyProcessLambda, keepByteCode, meta);
+ return getKotlinInfo(kotlin, clazz, factory, reporter, keepByteCode, meta);
}
return NO_KOTLIN_INFO;
}
@@ -49,14 +49,10 @@
DexClass clazz,
DexItemFactory factory,
Reporter reporter,
- boolean onlyProcessLambda,
Consumer<DexEncodedMethod> keepByteCode,
DexAnnotation annotation) {
try {
KotlinClassMetadata kMetadata = toKotlinClassMetadata(kotlin, annotation.annotation);
- if (onlyProcessLambda && !isSyntheticClassifiedLambda(kotlin, clazz, kMetadata)) {
- return NO_KOTLIN_INFO;
- }
return createKotlinInfo(kotlin, clazz, kMetadata, factory, reporter, keepByteCode);
} catch (ClassCastException | InconsistentKotlinMetadataException | MetadataError e) {
reporter.info(
@@ -77,12 +73,18 @@
}
}
- private static boolean isSyntheticClassifiedLambda(
- Kotlin kotlin, DexClass clazz, KotlinClassMetadata kMetadata) {
- if (kMetadata instanceof SyntheticClass) {
- SyntheticClass syntheticClass = (SyntheticClass) kMetadata;
- return syntheticClass.isLambda()
- && getFlavour(syntheticClass, clazz, kotlin) != Flavour.Unclassified;
+ public static boolean isLambda(AppView<?> appView, DexClass clazz) {
+ DexItemFactory dexItemFactory = appView.dexItemFactory();
+ Kotlin kotlin = dexItemFactory.kotlin;
+ DexAnnotation metadataAnnotation =
+ clazz.annotations().getFirstMatching(dexItemFactory.kotlinMetadataType);
+ if (metadataAnnotation != null) {
+ KotlinClassMetadata kMetadata = toKotlinClassMetadata(kotlin, metadataAnnotation.annotation);
+ if (kMetadata instanceof SyntheticClass) {
+ SyntheticClass syntheticClass = (SyntheticClass) kMetadata;
+ return syntheticClass.isLambda()
+ && getFlavour(syntheticClass, clazz, kotlin) != Flavour.Unclassified;
+ }
}
return false;
}
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 f9d2d18..4eef6cb 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinMetadataEnqueuerExtension.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinMetadataEnqueuerExtension.java
@@ -18,6 +18,8 @@
import com.android.tools.r8.graph.EnclosingMethodAttribute;
import com.android.tools.r8.graph.ProgramDefinition;
import com.android.tools.r8.graph.analysis.EnqueuerAnalysis;
+import com.android.tools.r8.ir.optimize.info.OptimizationFeedback;
+import com.android.tools.r8.ir.optimize.info.OptimizationFeedbackSimple;
import com.android.tools.r8.shaking.Enqueuer;
import com.android.tools.r8.shaking.Enqueuer.EnqueuerDefinitionSupplier;
import com.google.common.collect.Sets;
@@ -25,6 +27,8 @@
public class KotlinMetadataEnqueuerExtension extends EnqueuerAnalysis {
+ private static final OptimizationFeedback feedback = OptimizationFeedbackSimple.getInstance();
+
private final AppView<?> appView;
private final EnqueuerDefinitionSupplier enqueuerDefinitionSupplier;
private final Set<DexType> prunedTypes;
@@ -57,23 +61,27 @@
Set<DexProgramClass> localOrAnonymousClasses = Sets.newIdentityHashSet();
enqueuer.forAllLiveClasses(
clazz -> {
- boolean onlyProcessLambdas = !keepMetadata || !enqueuer.isPinned(clazz.type);
assert clazz.getKotlinInfo().isNoKotlinInformation();
- clazz.setKotlinInfo(
- KotlinClassMetadataReader.getKotlinInfo(
- appView.dexItemFactory().kotlin,
- clazz,
- appView.dexItemFactory(),
- appView.options().reporter,
- onlyProcessLambdas,
- method -> keepByteCodeFunctions.add(method.method)));
- if (onlyProcessLambdas) {
+ if (!keepMetadata || !enqueuer.isPinned(clazz.getType())) {
+ if (KotlinClassMetadataReader.isLambda(appView, clazz)
+ && clazz.hasClassInitializer()) {
+ feedback.classInitializerMayBePostponed(clazz.getClassInitializer());
+ }
+ clazz.setKotlinInfo(NO_KOTLIN_INFO);
clazz.removeAnnotations(
annotation -> annotation.getAnnotationType() == kotlinMetadataType);
- }
- if (clazz.getEnclosingMethodAttribute() != null
- && clazz.getEnclosingMethodAttribute().getEnclosingMethod() != null) {
- localOrAnonymousClasses.add(clazz);
+ } else {
+ clazz.setKotlinInfo(
+ KotlinClassMetadataReader.getKotlinInfo(
+ appView.dexItemFactory().kotlin,
+ clazz,
+ appView.dexItemFactory(),
+ appView.options().reporter,
+ method -> keepByteCodeFunctions.add(method.getReference())));
+ if (clazz.getEnclosingMethodAttribute() != null
+ && clazz.getEnclosingMethodAttribute().getEnclosingMethod() != null) {
+ localOrAnonymousClasses.add(clazz);
+ }
}
});
appView.setCfByteCodePassThrough(keepByteCodeFunctions);
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 1001ca9..44d6ede 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinMetadataRewriter.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinMetadataRewriter.java
@@ -139,7 +139,7 @@
}
final KotlinClassLevelInfo kotlinInfo =
KotlinClassMetadataReader.getKotlinInfo(
- kotlin, clazz, factory, reporter, false, ConsumerUtils.emptyConsumer(), metadata);
+ kotlin, clazz, factory, reporter, ConsumerUtils.emptyConsumer(), metadata);
if (kotlinInfo == NO_KOTLIN_INFO) {
return;
}
diff --git a/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java b/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java
index baa1189..06c03e7 100644
--- a/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java
+++ b/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java
@@ -195,7 +195,7 @@
AppInfoWithLiveness(
CommittedItems syntheticItems,
ClassToFeatureSplitMap classToFeatureSplitMap,
- MainDexClasses mainDexClasses,
+ MainDexInfo mainDexInfo,
Set<DexType> deadProtoTypes,
MissingClasses missingClasses,
Set<DexType> liveTypes,
@@ -234,7 +234,7 @@
Map<DexField, Int2ReferenceMap<DexField>> switchMaps,
Set<DexType> lockCandidates,
Map<DexType, Visibility> initClassReferences) {
- super(syntheticItems, classToFeatureSplitMap, mainDexClasses, missingClasses);
+ super(syntheticItems, classToFeatureSplitMap, mainDexInfo, missingClasses);
this.deadProtoTypes = deadProtoTypes;
this.liveTypes = liveTypes;
this.targetedMethods = targetedMethods;
@@ -279,7 +279,7 @@
this(
committedItems,
previous.getClassToFeatureSplitMap(),
- previous.getMainDexClasses(),
+ previous.getMainDexInfo(),
previous.deadProtoTypes,
previous.getMissingClasses(),
CollectionUtils.mergeSets(previous.liveTypes, committedItems.getCommittedProgramTypes()),
@@ -324,7 +324,7 @@
this(
previous.getSyntheticItems().commitPrunedItems(prunedItems),
previous.getClassToFeatureSplitMap().withoutPrunedItems(prunedItems),
- previous.getMainDexClasses().withoutPrunedItems(prunedItems),
+ previous.getMainDexInfo().withoutPrunedItems(prunedItems),
previous.deadProtoTypes,
previous.getMissingClasses(),
prunedItems.hasRemovedClasses()
@@ -376,6 +376,52 @@
return true;
}
+ @Override
+ public AppInfoWithLiveness rebuildWithMainDexInfo(MainDexInfo mainDexInfo) {
+ return new AppInfoWithLiveness(
+ getSyntheticItems().commit(app()),
+ getClassToFeatureSplitMap(),
+ mainDexInfo,
+ deadProtoTypes,
+ getMissingClasses(),
+ liveTypes,
+ targetedMethods,
+ failedMethodResolutionTargets,
+ failedFieldResolutionTargets,
+ bootstrapMethods,
+ methodsTargetedByInvokeDynamic,
+ virtualMethodsTargetedByInvokeDirect,
+ liveMethods,
+ fieldAccessInfoCollection,
+ methodAccessInfoCollection,
+ objectAllocationInfoCollection,
+ callSites,
+ keepInfo,
+ mayHaveSideEffects,
+ noSideEffects,
+ assumedValues,
+ alwaysInline,
+ forceInline,
+ neverInline,
+ neverInlineDueToSingleCaller,
+ whyAreYouNotInlining,
+ keepConstantArguments,
+ keepUnusedArguments,
+ reprocess,
+ neverReprocess,
+ alwaysClassInline,
+ neverClassInline,
+ noClassMerging,
+ noVerticalClassMerging,
+ noHorizontalClassMerging,
+ neverPropagateValue,
+ identifierNameStrings,
+ prunedTypes,
+ switchMaps,
+ lockCandidates,
+ initClassReferences);
+ }
+
private static KeepInfoCollection extendPinnedItems(
AppInfoWithLiveness previous, Collection<? extends DexReference> additionalPinnedItems) {
if (additionalPinnedItems == null || additionalPinnedItems.isEmpty()) {
@@ -418,7 +464,7 @@
super(
previous.getSyntheticItems().commit(previous.app()),
previous.getClassToFeatureSplitMap(),
- previous.getMainDexClasses(),
+ previous.getMainDexInfo(),
previous.getMissingClasses());
this.deadProtoTypes = previous.deadProtoTypes;
this.liveTypes = previous.liveTypes;
@@ -999,7 +1045,7 @@
return new AppInfoWithLiveness(
committedItems,
getClassToFeatureSplitMap().rewrittenWithLens(lens),
- getMainDexClasses().rewrittenWithLens(lens),
+ getMainDexInfo().rewrittenWithLens(lens),
deadProtoTypes,
getMissingClasses().commitSyntheticItems(committedItems),
lens.rewriteTypes(liveTypes),
diff --git a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
index ce1dadb..9161f4f 100644
--- a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
+++ b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
@@ -61,6 +61,7 @@
import com.android.tools.r8.graph.NestMemberClassAttribute;
import com.android.tools.r8.graph.ObjectAllocationInfoCollectionImpl;
import com.android.tools.r8.graph.ProgramDefinition;
+import com.android.tools.r8.graph.ProgramDerivedContext;
import com.android.tools.r8.graph.ProgramField;
import com.android.tools.r8.graph.ProgramMember;
import com.android.tools.r8.graph.ProgramMethod;
@@ -149,7 +150,6 @@
import java.util.List;
import java.util.ListIterator;
import java.util.Map;
-import java.util.Map.Entry;
import java.util.Set;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
@@ -177,6 +177,7 @@
FINAL_TREE_SHAKING,
INITIAL_MAIN_DEX_TRACING,
FINAL_MAIN_DEX_TRACING,
+ GENERATE_MAIN_DEX_LIST,
WHY_ARE_YOU_KEEPING;
public boolean isInitialTreeShaking() {
@@ -199,8 +200,12 @@
return this == FINAL_MAIN_DEX_TRACING;
}
+ public boolean isGenerateMainDexList() {
+ return this == GENERATE_MAIN_DEX_LIST;
+ }
+
public boolean isMainDexTracing() {
- return isInitialMainDexTracing() || isFinalMainDexTracing();
+ return isInitialMainDexTracing() || isFinalMainDexTracing() || isGenerateMainDexList();
}
public boolean isWhyAreYouKeeping() {
@@ -345,11 +350,6 @@
private final Map<DexProgramClass, Set<DexMethod>> reachableVirtualTargets =
new IdentityHashMap<>();
- /**
- * A set of references we have reported missing to dedupe warnings.
- */
- private final Set<DexReference> reportedMissing = Sets.newIdentityHashSet();
-
/** Collection of keep requirements for the program. */
private final MutableKeepInfoCollection keepInfo = new MutableKeepInfoCollection();
@@ -398,15 +398,13 @@
private final Map<DexMethod, ProgramMethod> methodsWithTwrCloseResource = new IdentityHashMap<>();
private final Set<DexProgramClass> classesWithSerializableLambdas = Sets.newIdentityHashSet();
private final ProgramMethodSet pendingDesugaring = ProgramMethodSet.create();
- private final MainDexTracingResult previousMainDexTracingResult;
Enqueuer(
AppView<? extends AppInfoWithClassHierarchy> appView,
ExecutorService executorService,
SubtypingInfo subtypingInfo,
GraphConsumer keptGraphConsumer,
- Mode mode,
- MainDexTracingResult previousMainDexTracingResult) {
+ Mode mode) {
assert appView.appServices() != null;
InternalOptions options = appView.options();
this.appInfo = appView.appInfo();
@@ -424,7 +422,6 @@
mode.isInitialTreeShaking() && options.forceProguardCompatibility
? ProguardCompatibilityActions.builder()
: null;
- this.previousMainDexTracingResult = previousMainDexTracingResult;
if (mode.isInitialOrFinalTreeShaking()) {
if (options.protoShrinking().enableGeneratedMessageLiteShrinking) {
@@ -549,8 +546,14 @@
recordTypeReference(type, context, this::reportMissingClass);
}
+ private void recordTypeReference(DexType type, ProgramDerivedContext context) {
+ recordTypeReference(type, context, this::reportMissingClass);
+ }
+
private void recordTypeReference(
- DexType type, ProgramDefinition context, Consumer<DexType> missingClassConsumer) {
+ DexType type,
+ ProgramDerivedContext context,
+ BiConsumer<DexType, ProgramDerivedContext> missingClassConsumer) {
if (type == null) {
return;
}
@@ -569,7 +572,9 @@
}
private void recordMethodReference(
- DexMethod method, ProgramDefinition context, Consumer<DexType> missingClassConsumer) {
+ DexMethod method,
+ ProgramDefinition context,
+ BiConsumer<DexType, ProgramDerivedContext> missingClassConsumer) {
recordTypeReference(method.holder, context, missingClassConsumer);
recordTypeReference(method.proto.returnType, context, missingClassConsumer);
for (DexType type : method.proto.parameters.values) {
@@ -577,9 +582,9 @@
}
}
- private void recordFieldReference(DexField field, ProgramDefinition context) {
- recordTypeReference(field.holder, context);
- recordTypeReference(field.type, context);
+ private void recordFieldReference(DexField field, ProgramDerivedContext context) {
+ recordTypeReference(field.getHolderType(), context);
+ recordTypeReference(field.getType(), context);
}
public DexEncodedMethod definitionFor(DexMethod method, ProgramDefinition context) {
@@ -595,15 +600,19 @@
}
private DexClass definitionFor(
- DexType type, ProgramDefinition context, Consumer<DexType> missingClassConsumer) {
+ DexType type,
+ ProgramDerivedContext context,
+ BiConsumer<DexType, ProgramDerivedContext> missingClassConsumer) {
return internalDefinitionFor(type, context, missingClassConsumer);
}
private DexClass internalDefinitionFor(
- DexType type, ProgramDefinition context, Consumer<DexType> missingClassConsumer) {
+ DexType type,
+ ProgramDerivedContext context,
+ BiConsumer<DexType, ProgramDerivedContext> missingClassConsumer) {
DexClass clazz = appInfo().definitionFor(type);
if (clazz == null) {
- missingClassConsumer.accept(type);
+ missingClassConsumer.accept(type, context);
return null;
}
if (clazz.isNotProgramClass()) {
@@ -661,7 +670,7 @@
}
DexClass definition = appView.definitionFor(type);
if (definition == null) {
- reportMissingClass(type);
+ reportMissingClassWithoutContext(type);
return;
}
if (definition.isProgramClass() || !liveNonProgramTypes.add(definition)) {
@@ -729,20 +738,6 @@
items.forEachClass(this::enqueueRootClass);
}
- private void enqueueRootItem(Entry<DexReference, Set<ProguardKeepRuleBase>> root) {
- DexReference reference = root.getKey();
- Set<ProguardKeepRuleBase> rules = root.getValue();
- if (reference.isDexField()) {
- enqueueRootField(reference.asDexField(), rules);
- } else if (reference.isDexMethod()) {
- enqueueRootMethod(reference.asDexMethod(), rules);
- } else if (reference.isDexType()) {
- enqueueRootClass(reference.asDexType(), rules);
- } else {
- throw new Unreachable();
- }
- }
-
// TODO(b/123923324): Verify that root items are present.
private void enqueueRootClass(DexType type, Set<ProguardKeepRuleBase> rules) {
DexProgramClass clazz = asProgramClassOrNull(appView.definitionFor(type));
@@ -1441,11 +1436,10 @@
return;
}
- // Must mark the field as targeted even if it does not exist.
- markFieldAsTargeted(fieldReference, currentMethod);
-
FieldResolutionResult resolutionResult = resolveField(fieldReference, currentMethod);
if (resolutionResult.isFailedOrUnknownResolution()) {
+ // Must mark the field as targeted even if it does not exist.
+ markFieldAsTargeted(fieldReference, currentMethod);
noClassMerging.add(fieldReference.getHolderType());
return;
}
@@ -1471,15 +1465,12 @@
Log.verbose(getClass(), "Register Iget `%s`.", fieldReference);
}
- // If unused interface removal is enabled, then we won't necessarily mark the actual holder of
- // the field as live, if the holder is an interface.
- if (appView.options().enableUnusedInterfaceRemoval) {
- if (field.getReference() != fieldReference) {
- markTypeAsLive(field.getHolder(), currentMethod);
- }
+ if (field.getReference() != fieldReference) {
+ // Mark the initial resolution holder as live.
+ markTypeAsLive(resolutionResult.getInitialResolutionHolder(), currentMethod);
}
- workList.enqueueMarkReachableFieldAction(
+ workList.enqueueMarkInstanceFieldAsReachableAction(
field, currentMethod, KeepReason.fieldReferencedIn(currentMethod));
}
@@ -1497,11 +1488,10 @@
return;
}
- // Must mark the field as targeted even if it does not exist.
- markFieldAsTargeted(fieldReference, currentMethod);
-
FieldResolutionResult resolutionResult = resolveField(fieldReference, currentMethod);
if (resolutionResult.isFailedOrUnknownResolution()) {
+ // Must mark the field as targeted even if it does not exist.
+ markFieldAsTargeted(fieldReference, currentMethod);
noClassMerging.add(fieldReference.getHolderType());
return;
}
@@ -1527,16 +1517,13 @@
Log.verbose(getClass(), "Register Iput `%s`.", fieldReference);
}
- // If unused interface removal is enabled, then we won't necessarily mark the actual holder of
- // the field as live, if the holder is an interface.
- if (appView.options().enableUnusedInterfaceRemoval) {
- if (field.getReference() != fieldReference) {
- markTypeAsLive(field.getHolder(), currentMethod);
- }
+ if (field.getReference() != fieldReference) {
+ // Mark the initial resolution holder as live.
+ markTypeAsLive(resolutionResult.getInitialResolutionHolder(), currentMethod);
}
KeepReason reason = KeepReason.fieldReferencedIn(currentMethod);
- workList.enqueueMarkReachableFieldAction(field, currentMethod, reason);
+ workList.enqueueMarkInstanceFieldAsReachableAction(field, currentMethod, reason);
}
void traceStaticFieldRead(DexField field, ProgramMethod currentMethod) {
@@ -1598,9 +1585,9 @@
}
if (field.getReference() != fieldReference) {
- // Mark the non-rebound field access as targeted. Note that this should only be done if the
- // field is not a dead proto field (in which case we bail-out above).
- markFieldAsTargeted(fieldReference, currentMethod);
+ // Mark the initial resolution holder as live. Note that this should only be done if the field
+ // is not a dead proto field (in which case we bail-out above).
+ markTypeAsLive(resolutionResult.getInitialResolutionHolder(), currentMethod);
}
markStaticFieldAsLive(field, currentMethod);
@@ -1663,9 +1650,9 @@
}
if (field.getReference() != fieldReference) {
- // Mark the non-rebound field access as targeted. Note that this should only be done if the
- // field is not a dead proto field (in which case we bail-out above).
- markFieldAsTargeted(fieldReference, currentMethod);
+ // Mark the initial resolution holder as live. Note that this should only be done if the field
+ // is not a dead proto field (in which case we bail-out above).
+ markTypeAsLive(resolutionResult.getInitialResolutionHolder(), currentMethod);
}
markStaticFieldAsLive(field, currentMethod);
@@ -1733,6 +1720,13 @@
markTypeAsLive(clazz, reason);
}
+ private void markTypeAsLive(DexClass clazz, ProgramDefinition context) {
+ if (clazz.isProgramClass()) {
+ DexProgramClass programClass = clazz.asProgramClass();
+ markTypeAsLive(programClass, graphReporter.reportClassReferencedFrom(programClass, context));
+ }
+ }
+
private void markTypeAsLive(DexProgramClass clazz, ProgramDefinition context) {
markTypeAsLive(clazz, graphReporter.reportClassReferencedFrom(clazz, context));
}
@@ -1754,14 +1748,20 @@
assert !mode.isFinalMainDexTracing()
|| !options.testing.checkForNotExpandingMainDexTracingResult
- || previousMainDexTracingResult.isRoot(clazz)
+ || appView.appInfo().getMainDexInfo().isTracedRoot(clazz)
|| clazz.toSourceString().contains(ENUM_UNBOXING_UTILITY_CLASS_SUFFIX)
: "Class " + clazz.toSourceString() + " was not a main dex root in the first round";
// Mark types in inner-class attributes referenced.
- for (InnerClassAttribute innerClassAttribute : clazz.getInnerClasses()) {
- recordTypeReference(innerClassAttribute.getInner(), clazz, this::ignoreMissingClass);
- recordTypeReference(innerClassAttribute.getOuter(), clazz, this::ignoreMissingClass);
+ {
+ BiConsumer<DexType, ProgramDerivedContext> missingClassConsumer =
+ options.reportMissingClassesInInnerClassAttributes
+ ? this::reportMissingClass
+ : this::ignoreMissingClass;
+ for (InnerClassAttribute innerClassAttribute : clazz.getInnerClasses()) {
+ recordTypeReference(innerClassAttribute.getInner(), clazz, missingClassConsumer);
+ recordTypeReference(innerClassAttribute.getOuter(), clazz, missingClassConsumer);
+ }
}
// Mark types in nest attributes referenced.
@@ -1777,11 +1777,15 @@
EnclosingMethodAttribute enclosingMethodAttribute = clazz.getEnclosingMethodAttribute();
if (enclosingMethodAttribute != null) {
DexMethod enclosingMethod = enclosingMethodAttribute.getEnclosingMethod();
+ BiConsumer<DexType, ProgramDerivedContext> missingClassConsumer =
+ options.reportMissingClassesInEnclosingMethodAttribute
+ ? this::reportMissingClass
+ : this::ignoreMissingClass;
if (enclosingMethod != null) {
- recordMethodReference(enclosingMethod, clazz, this::ignoreMissingClass);
+ recordMethodReference(enclosingMethod, clazz, missingClassConsumer);
} else {
recordTypeReference(
- enclosingMethodAttribute.getEnclosingClass(), clazz, this::ignoreMissingClass);
+ enclosingMethodAttribute.getEnclosingClass(), clazz, missingClassConsumer);
}
}
@@ -1951,8 +1955,8 @@
DexProgramClass holder, ProgramDefinition annotatedItem, DexAnnotation annotation) {
assert annotatedItem == holder || annotatedItem.asProgramMember().getHolder() == holder;
assert !holder.isDexClass() || holder.asDexClass().isProgramClass();
- DexType type = annotation.annotation.type;
- recordTypeReference(type, holder);
+ DexType type = annotation.getAnnotationType();
+ recordTypeReference(type, annotatedItem);
DexClass clazz = appView.definitionFor(type);
boolean annotationTypeIsLibraryClass = clazz == null || clazz.isNotProgramClass();
boolean isLive = annotationTypeIsLibraryClass || liveTypes.contains(clazz.asProgramClass());
@@ -1967,17 +1971,20 @@
graphReporter.registerAnnotation(annotation, reason);
AnnotationReferenceMarker referenceMarker =
new AnnotationReferenceMarker(
- annotation.annotation.type, holder, appView.dexItemFactory(), reason);
+ annotation.getAnnotationType(), annotatedItem, appView.dexItemFactory(), reason);
annotation.annotation.collectIndexedItems(referenceMarker);
}
private FieldResolutionResult resolveField(DexField field, ProgramDefinition context) {
// Record the references in case they are not program types.
- recordFieldReference(field, context);
FieldResolutionResult resolutionResult = appInfo.resolveField(field);
- if (resolutionResult.isFailedOrUnknownResolution()) {
- reportMissingField(field);
+ if (resolutionResult.isSuccessfulResolution()) {
+ recordFieldReference(
+ field, resolutionResult.getResolutionPair().asProgramDerivedContext(context));
+ } else {
+ assert resolutionResult.isFailedOrUnknownResolution();
failedFieldResolutionTargets.add(field);
+ recordFieldReference(field, context);
}
return resolutionResult;
}
@@ -1988,7 +1995,6 @@
recordMethodReference(method, context);
ResolutionResult resolutionResult = appInfo.unsafeResolveMethodDueToDexFormat(method);
if (resolutionResult.isFailedResolution()) {
- reportMissingMethod(method);
markFailedMethodResolutionTargets(
method, resolutionResult.asFailedResolution(), context, reason);
}
@@ -2001,7 +2007,6 @@
recordMethodReference(method, context);
ResolutionResult resolutionResult = appInfo.resolveMethod(method, interfaceInvoke);
if (resolutionResult.isFailedResolution()) {
- reportMissingMethod(method);
markFailedMethodResolutionTargets(
method, resolutionResult.asFailedResolution(), context, reason);
}
@@ -2142,7 +2147,6 @@
DexEncodedMethod encodedMethod = clazz.lookupMethod(reference);
if (encodedMethod == null) {
failedMethodResolutionTargets.add(reference);
- reportMissingMethod(reference);
return;
}
@@ -2243,7 +2247,11 @@
missingClassesBuilder.ignoreNewMissingClass(clazz);
}
- private void reportMissingClass(DexType clazz) {
+ private void ignoreMissingClass(DexType clazz, ProgramDerivedContext context) {
+ ignoreMissingClass(clazz);
+ }
+
+ private void reportMissingClass(DexType clazz, ProgramDerivedContext context) {
assert !mode.isFinalTreeShaking()
|| missingClassesBuilder.wasAlreadyMissing(clazz)
|| appView.dexItemFactory().isPossiblyCompilerSynthesizedType(clazz)
@@ -2251,19 +2259,19 @@
// TODO(b/157107464): See if we can clean this up.
|| (initialPrunedTypes != null && initialPrunedTypes.contains(clazz))
: "Unexpected missing class `" + clazz.toSourceString() + "`";
- missingClassesBuilder.addNewMissingClass(clazz);
+ missingClassesBuilder.addNewMissingClass(clazz, context);
}
- private void reportMissingMethod(DexMethod method) {
- if (Log.ENABLED && reportedMissing.add(method)) {
- Log.verbose(Enqueuer.class, "Method `%s` is missing.", method);
- }
- }
-
- private void reportMissingField(DexField field) {
- if (Log.ENABLED && reportedMissing.add(field)) {
- Log.verbose(Enqueuer.class, "Field `%s` is missing.", field);
- }
+ @Deprecated
+ private void reportMissingClassWithoutContext(DexType clazz) {
+ assert !mode.isFinalTreeShaking()
+ || missingClassesBuilder.wasAlreadyMissing(clazz)
+ || appView.dexItemFactory().isPossiblyCompilerSynthesizedType(clazz)
+ || initialDeadProtoTypes.contains(clazz)
+ // TODO(b/157107464): See if we can clean this up.
+ || (initialPrunedTypes != null && initialPrunedTypes.contains(clazz))
+ : "Unexpected missing class `" + clazz.toSourceString() + "`";
+ missingClassesBuilder.legacyAddNewMissingClass(clazz);
}
private void markMethodAsTargeted(ProgramMethod method, KeepReason reason) {
@@ -2644,9 +2652,14 @@
}
}
+ private void markFieldAsTargeted(ProgramField field) {
+ markTypeAsLive(field.getHolder(), field);
+ markTypeAsLive(field.getType(), field);
+ }
+
private void markFieldAsTargeted(DexField field, ProgramMethod context) {
- markTypeAsLive(field.type, context);
- markTypeAsLive(field.holder, context);
+ markTypeAsLive(field.getHolderType(), context);
+ markTypeAsLive(field.getType(), context);
}
private void markStaticFieldAsLive(ProgramField field, ProgramMethod context) {
@@ -2655,9 +2668,8 @@
private void markStaticFieldAsLive(
ProgramField field, ProgramDefinition context, KeepReason reason) {
- // Mark the type live here, so that the class exists at runtime.
- markTypeAsLive(field.getHolder(), field);
- markTypeAsLive(field.getReference().type, field);
+ // Mark the field type and holder live here, so that they exist at runtime.
+ markFieldAsTargeted(field);
markDirectAndIndirectClassInitializersAsLive(field.getHolder());
@@ -2687,8 +2699,8 @@
private void markInstanceFieldAsLive(
ProgramField field, ProgramDefinition context, KeepReason reason) {
- markTypeAsLive(field.getHolder(), field);
- markTypeAsLive(field.getType(), field);
+ markFieldAsTargeted(field);
+
if (Log.ENABLED) {
Log.verbose(getClass(), "Adding instance field `%s` to live set.", field);
}
@@ -2816,8 +2828,7 @@
field.getHolder())) {
markInstanceFieldAsLive(field, context, reason);
} else {
- markTypeAsLive(field.getHolder(), field);
- markTypeAsLive(field.getReference().type, field);
+ markFieldAsTargeted(field);
// Add the field to the reachable set if the type later becomes instantiated.
reachableInstanceFields
@@ -3014,16 +3025,27 @@
}
// Returns the set of live types.
- public Set<DexProgramClass> traceMainDex(
- RootSet rootSet, ExecutorService executorService, Timing timing) throws ExecutionException {
+ public MainDexInfo traceMainDex(ExecutorService executorService, Timing timing)
+ throws ExecutionException {
assert analyses.isEmpty();
assert mode.isMainDexTracing();
- this.rootSet = rootSet;
+ this.rootSet = appView.getMainDexRootSet();
// Translate the result of root-set computation into enqueuer actions.
enqueueRootItems(rootSet.noShrinking);
trace(executorService, timing);
options.reporter.failIfPendingErrors();
- return liveTypes.getItems();
+ // Calculate the automatic main dex list according to legacy multidex constraints.
+ MainDexInfo.Builder builder = appView.appInfo().getMainDexInfo().builder();
+ liveTypes.getItems().forEach(builder::addRoot);
+ if (mode.isInitialMainDexTracing()) {
+ liveMethods.getItems().forEach(method -> builder.addRoot(method.method));
+ } else {
+ assert appView.appInfo().getMainDexInfo().isTracedMethodRootsCleared()
+ || mode.isGenerateMainDexList();
+ }
+ new MainDexListBuilder(appView, builder.getRoots(), builder).run();
+ MainDexInfo previousMainDexInfo = appInfo.getMainDexInfo();
+ return builder.build(previousMainDexInfo);
}
public AppInfoWithLiveness traceApplication(
@@ -3155,8 +3177,6 @@
Map<DexMethod, ProgramMethod> liveMethods = new IdentityHashMap<>();
- Map<DexType, DexProgramClass> syntheticProgramClasses = new IdentityHashMap<>();
-
Map<DexType, DexClasspathClass> syntheticClasspathClasses = new IdentityHashMap<>();
// Subset of live methods that need have keep requirements.
@@ -3171,7 +3191,6 @@
desugaredMethods.isEmpty()
&& syntheticInstantiations.isEmpty()
&& liveMethods.isEmpty()
- && syntheticProgramClasses.isEmpty()
&& syntheticClasspathClasses.isEmpty();
assert !empty || (liveMethodsWithKeepActions.isEmpty() && mainDexTypes.isEmpty());
return empty;
@@ -3187,11 +3206,6 @@
assert old == null;
}
- void addProgramClass(DexProgramClass clazz) {
- DexProgramClass old = syntheticProgramClasses.put(clazz.type, clazz);
- assert old == null;
- }
-
void addLiveMethod(ProgramMethod method) {
DexMethod signature = method.getDefinition().method;
assert !liveMethods.containsKey(signature);
@@ -3206,13 +3220,12 @@
void amendApplication(Builder appBuilder) {
assert !isEmpty();
- appBuilder.addProgramClasses(syntheticProgramClasses.values());
appBuilder.addClasspathClasses(syntheticClasspathClasses.values());
}
- void amendMainDexClasses(MainDexClasses mainDexClasses) {
+ void amendMainDexClasses(MainDexInfo mainDexInfo) {
assert !isEmpty();
- mainDexClasses.addAll(mainDexTypes);
+ mainDexTypes.forEach(mainDexInfo::addSyntheticClass);
}
void enqueueWorkItems(Enqueuer enqueuer) {
@@ -3283,7 +3296,6 @@
synthesizeLambdas(additions);
synthesizeLibraryConversionWrappers(additions);
synthesizeBackports(additions);
- synthesizeNestConstructor(additions);
synthesizeTwrCloseResource(additions);
if (additions.isEmpty()) {
return;
@@ -3297,7 +3309,7 @@
additions.amendApplication(appBuilder);
return appBuilder.build();
});
- additions.amendMainDexClasses(appInfo.getMainDexClasses());
+ additions.amendMainDexClasses(appInfo.getMainDexInfo());
appView.setAppInfo(appInfo);
subtypingInfo = new SubtypingInfo(appView);
@@ -3372,16 +3384,6 @@
}
}
- private void synthesizeNestConstructor(SyntheticAdditions additions) {
- if (nestBasedAccessRewriter != null) {
- DexProgramClass nestConstructor = nestBasedAccessRewriter.synthesizeNestConstructor();
- if (nestConstructor != null) {
- // TODO(b/177638147): use getSyntheticItems().createClass().
- additions.addProgramClass(nestConstructor);
- }
- }
- }
-
private void synthesizeTwrCloseResource(SyntheticAdditions additions) {
for (ProgramMethod method : methodsWithTwrCloseResource.values()) {
twrCloseResourceRewriter.rewriteCf(method, additions::addLiveMethod);
@@ -3501,7 +3503,7 @@
new AppInfoWithLiveness(
appInfo.getSyntheticItems().commit(app),
appInfo.getClassToFeatureSplitMap(),
- appInfo.getMainDexClasses(),
+ appInfo.getMainDexInfo(),
deadProtoTypes,
appView.testing().enableExperimentalMissingClassesReporting
? missingClassesBuilder.reportMissingClasses(appView)
@@ -4107,15 +4109,10 @@
DexProgramClass clazz, Supplier<KeepReason> reasonSupplier) {
assert forceProguardCompatibility;
- if (appView.hasProguardCompatibilityActions()
- && !appView.getProguardCompatibilityActions().isCompatInstantiated(clazz)) {
+ if (!addCompatInstantiatedClass(clazz)) {
return;
}
- if (mode.isInitialTreeShaking()) {
- proguardCompatibilityActionsBuilder.addCompatInstantiatedType(clazz);
- }
-
KeepReasonWitness witness = graphReporter.registerClass(clazz, reasonSupplier.get());
if (clazz.isAnnotation()) {
markTypeAsLive(clazz, witness);
@@ -4133,6 +4130,25 @@
}
}
+ private boolean addCompatInstantiatedClass(DexProgramClass clazz) {
+ assert forceProguardCompatibility;
+
+ // During the first round of tree shaking, we compat-instantiate all classes referenced from
+ // check-cast, const-class, and instance-of instructions.
+ if (mode.isInitialTreeShaking()) {
+ proguardCompatibilityActionsBuilder.addCompatInstantiatedType(clazz);
+ return true;
+ }
+
+ assert proguardCompatibilityActionsBuilder == null;
+
+ // Otherwise, we only compat-instantiate classes referenced from check-cast, const-class, and
+ // instance-of instructions that were also compat-instantiated during the first round of tree
+ // shaking.
+ return appView.hasProguardCompatibilityActions()
+ && appView.getProguardCompatibilityActions().isCompatInstantiated(clazz);
+ }
+
private void markMethodAsLiveWithCompatRule(ProgramMethod method) {
enqueueMarkMethodLiveAction(method, method, graphReporter.reportCompatKeepMethod(method));
}
diff --git a/src/main/java/com/android/tools/r8/shaking/EnqueuerFactory.java b/src/main/java/com/android/tools/r8/shaking/EnqueuerFactory.java
index 04fb840..cfce371 100644
--- a/src/main/java/com/android/tools/r8/shaking/EnqueuerFactory.java
+++ b/src/main/java/com/android/tools/r8/shaking/EnqueuerFactory.java
@@ -19,13 +19,7 @@
AppView<? extends AppInfoWithClassHierarchy> appView,
ExecutorService executorService,
SubtypingInfo subtypingInfo) {
- return new Enqueuer(
- appView,
- executorService,
- subtypingInfo,
- null,
- Mode.INITIAL_TREE_SHAKING,
- MainDexTracingResult.NONE);
+ return new Enqueuer(appView, executorService, subtypingInfo, null, Mode.INITIAL_TREE_SHAKING);
}
public static Enqueuer createForFinalTreeShaking(
@@ -36,12 +30,7 @@
Set<DexType> initialPrunedTypes) {
Enqueuer enqueuer =
new Enqueuer(
- appView,
- executorService,
- subtypingInfo,
- keptGraphConsumer,
- Mode.FINAL_TREE_SHAKING,
- MainDexTracingResult.NONE);
+ appView, executorService, subtypingInfo, keptGraphConsumer, Mode.FINAL_TREE_SHAKING);
appView.withProtoShrinker(
shrinker -> enqueuer.setInitialDeadProtoTypes(shrinker.getDeadProtoTypes()));
enqueuer.setInitialPrunedTypes(initialPrunedTypes);
@@ -53,27 +42,25 @@
ExecutorService executorService,
SubtypingInfo subtypingInfo) {
return new Enqueuer(
- appView,
- executorService,
- subtypingInfo,
- null,
- Mode.INITIAL_MAIN_DEX_TRACING,
- MainDexTracingResult.NONE);
+ appView, executorService, subtypingInfo, null, Mode.INITIAL_MAIN_DEX_TRACING);
}
public static Enqueuer createForFinalMainDexTracing(
AppView<? extends AppInfoWithClassHierarchy> appView,
ExecutorService executorService,
SubtypingInfo subtypingInfo,
- GraphConsumer keptGraphConsumer,
- MainDexTracingResult previousMainDexTracingResult) {
+ GraphConsumer keptGraphConsumer) {
return new Enqueuer(
- appView,
- executorService,
- subtypingInfo,
- keptGraphConsumer,
- Mode.FINAL_MAIN_DEX_TRACING,
- previousMainDexTracingResult);
+ appView, executorService, subtypingInfo, keptGraphConsumer, Mode.FINAL_MAIN_DEX_TRACING);
+ }
+
+ public static Enqueuer createForGenerateMainDexList(
+ AppView<? extends AppInfoWithClassHierarchy> appView,
+ ExecutorService executorService,
+ SubtypingInfo subtypingInfo,
+ GraphConsumer keptGraphConsumer) {
+ return new Enqueuer(
+ appView, executorService, subtypingInfo, keptGraphConsumer, Mode.GENERATE_MAIN_DEX_LIST);
}
public static Enqueuer createForWhyAreYouKeeping(
@@ -82,11 +69,6 @@
SubtypingInfo subtypingInfo,
GraphConsumer keptGraphConsumer) {
return new Enqueuer(
- appView,
- executorService,
- subtypingInfo,
- keptGraphConsumer,
- Mode.WHY_ARE_YOU_KEEPING,
- MainDexTracingResult.NONE);
+ appView, executorService, subtypingInfo, keptGraphConsumer, Mode.WHY_ARE_YOU_KEEPING);
}
}
diff --git a/src/main/java/com/android/tools/r8/shaking/EnqueuerWorklist.java b/src/main/java/com/android/tools/r8/shaking/EnqueuerWorklist.java
index b403b54..01e415b 100644
--- a/src/main/java/com/android/tools/r8/shaking/EnqueuerWorklist.java
+++ b/src/main/java/com/android/tools/r8/shaking/EnqueuerWorklist.java
@@ -55,13 +55,13 @@
}
}
- static class MarkReachableFieldAction extends EnqueuerAction {
+ static class MarkInstanceFieldAsReachableAction extends EnqueuerAction {
private final ProgramField field;
// TODO(b/175854431): Avoid pushing context on worklist.
private final ProgramDefinition context;
private final KeepReason reason;
- public MarkReachableFieldAction(
+ public MarkInstanceFieldAsReachableAction(
ProgramField field, ProgramDefinition context, KeepReason reason) {
this.field = field;
this.context = context;
@@ -276,9 +276,9 @@
queue.add(new MarkReachableSuperAction(method, from));
}
- public void enqueueMarkReachableFieldAction(
+ public void enqueueMarkInstanceFieldAsReachableAction(
ProgramField field, ProgramDefinition context, KeepReason reason) {
- queue.add(new MarkReachableFieldAction(field, context, reason));
+ queue.add(new MarkInstanceFieldAsReachableAction(field, context, reason));
}
// TODO(b/142378367): Context is the containing method that is cause of the instantiation.
diff --git a/src/main/java/com/android/tools/r8/shaking/MainDexClasses.java b/src/main/java/com/android/tools/r8/shaking/MainDexClasses.java
deleted file mode 100644
index 9c1c529..0000000
--- a/src/main/java/com/android/tools/r8/shaking/MainDexClasses.java
+++ /dev/null
@@ -1,118 +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.shaking;
-
-import com.android.tools.r8.graph.DexProgramClass;
-import com.android.tools.r8.graph.DexType;
-import com.android.tools.r8.graph.GraphLens;
-import com.android.tools.r8.graph.PrunedItems;
-import com.google.common.collect.Sets;
-import java.util.Set;
-import java.util.function.Consumer;
-
-public class MainDexClasses {
-
- private final Set<DexType> mainDexClasses;
-
- private MainDexClasses() {
- this(Sets.newIdentityHashSet());
- }
-
- private MainDexClasses(Set<DexType> mainDexClasses) {
- this.mainDexClasses = mainDexClasses;
- }
-
- public static Builder builder() {
- return new Builder();
- }
-
- public static MainDexClasses createEmptyMainDexClasses() {
- return new MainDexClasses();
- }
-
- public void add(DexProgramClass clazz) {
- mainDexClasses.add(clazz.getType());
- }
-
- public void addAll(MainDexClasses other) {
- mainDexClasses.addAll(other.mainDexClasses);
- }
-
- public void addAll(MainDexTracingResult other) {
- mainDexClasses.addAll(other.getClasses());
- }
-
- public void addAll(Iterable<DexProgramClass> classes) {
- for (DexProgramClass clazz : classes) {
- add(clazz);
- }
- }
-
- public boolean contains(DexType type) {
- return mainDexClasses.contains(type);
- }
-
- public boolean contains(DexProgramClass clazz) {
- return contains(clazz.getType());
- }
-
- public boolean containsAnyOf(Iterable<DexProgramClass> classes) {
- for (DexProgramClass clazz : classes) {
- if (contains(clazz)) {
- return true;
- }
- }
- return false;
- }
-
- public void forEach(Consumer<DexType> fn) {
- mainDexClasses.forEach(fn);
- }
-
- public boolean isEmpty() {
- return mainDexClasses.isEmpty();
- }
-
- public MainDexClasses rewrittenWithLens(GraphLens lens) {
- MainDexClasses rewrittenMainDexClasses = createEmptyMainDexClasses();
- for (DexType mainDexClass : mainDexClasses) {
- DexType rewrittenMainDexClass = lens.lookupType(mainDexClass);
- rewrittenMainDexClasses.mainDexClasses.add(rewrittenMainDexClass);
- }
- return rewrittenMainDexClasses;
- }
-
- public int size() {
- return mainDexClasses.size();
- }
-
- public MainDexClasses withoutPrunedItems(PrunedItems prunedItems) {
- if (prunedItems.isEmpty()) {
- return this;
- }
- MainDexClasses mainDexClassesAfterPruning = createEmptyMainDexClasses();
- for (DexType mainDexClass : mainDexClasses) {
- if (!prunedItems.getRemovedClasses().contains(mainDexClass)) {
- mainDexClassesAfterPruning.mainDexClasses.add(mainDexClass);
- }
- }
- return mainDexClassesAfterPruning;
- }
-
- public static class Builder {
-
- private final Set<DexType> mainDexClasses = Sets.newIdentityHashSet();
-
- private Builder() {}
-
- public void add(DexProgramClass clazz) {
- mainDexClasses.add(clazz.getType());
- }
-
- public MainDexClasses build() {
- return new MainDexClasses(mainDexClasses);
- }
- }
-}
diff --git a/src/main/java/com/android/tools/r8/shaking/MainDexDirectReferenceTracer.java b/src/main/java/com/android/tools/r8/shaking/MainDexDirectReferenceTracer.java
index 6f7cade..459dea6 100644
--- a/src/main/java/com/android/tools/r8/shaking/MainDexDirectReferenceTracer.java
+++ b/src/main/java/com/android/tools/r8/shaking/MainDexDirectReferenceTracer.java
@@ -25,6 +25,7 @@
import com.android.tools.r8.utils.Box;
import java.util.Set;
import java.util.function.Consumer;
+import java.util.function.Predicate;
public class MainDexDirectReferenceTracer {
private final AnnotationDirectReferenceCollector annotationDirectReferenceCollector =
@@ -66,19 +67,19 @@
method.registerCodeReferences(codeDirectReferenceCollector);
}
- public static boolean hasReferencesOutsideFromCode(
- AppInfoWithClassHierarchy appInfo, ProgramMethod method, Set<DexType> classes) {
- return getFirstReferenceOutsideFromCode(appInfo, method, classes) != null;
+ public static boolean hasReferencesOutsideMainDexClasses(
+ AppInfoWithClassHierarchy appInfo, ProgramMethod method, Predicate<DexType> isOutside) {
+ return getFirstReferenceOutsideFromCode(appInfo, method, isOutside) != null;
}
public static DexProgramClass getFirstReferenceOutsideFromCode(
- AppInfoWithClassHierarchy appInfo, ProgramMethod method, Set<DexType> classes) {
+ AppInfoWithClassHierarchy appInfo, ProgramMethod method, Predicate<DexType> isOutside) {
Box<DexProgramClass> result = new Box<>();
new MainDexDirectReferenceTracer(
appInfo,
type -> {
DexType baseType = type.toBaseType(appInfo.dexItemFactory());
- if (baseType.isClassType() && !classes.contains(baseType)) {
+ if (baseType.isClassType() && isOutside.test(baseType)) {
DexClass cls = appInfo.definitionFor(baseType);
if (cls != null && cls.isProgramClass()) {
result.set(cls.asProgramClass());
diff --git a/src/main/java/com/android/tools/r8/shaking/MainDexInfo.java b/src/main/java/com/android/tools/r8/shaking/MainDexInfo.java
new file mode 100644
index 0000000..58315d4
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/shaking/MainDexInfo.java
@@ -0,0 +1,427 @@
+// 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.shaking;
+
+import static com.android.tools.r8.shaking.MainDexInfo.MainDexGroup.MAIN_DEX_ROOT;
+import static com.android.tools.r8.utils.LensUtils.rewriteAndApplyIfNotPrimitiveType;
+import static com.android.tools.r8.utils.PredicateUtils.not;
+
+import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.graph.DexReference;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.GraphLens;
+import com.android.tools.r8.graph.ProgramDefinition;
+import com.android.tools.r8.graph.ProgramMethod;
+import com.android.tools.r8.graph.PrunedItems;
+import com.android.tools.r8.utils.ConsumerUtils;
+import com.android.tools.r8.utils.SetUtils;
+import com.google.common.collect.Sets;
+import java.util.Collections;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.function.Consumer;
+
+public class MainDexInfo {
+
+ private static final MainDexInfo NONE =
+ new MainDexInfo(
+ Collections.emptySet(),
+ Collections.emptySet(),
+ Collections.emptySet(),
+ Collections.emptySet(),
+ Collections.emptySet(),
+ false);
+
+ public enum MainDexGroup {
+ MAIN_DEX_LIST,
+ MAIN_DEX_ROOT,
+ MAIN_DEX_DEPENDENCY,
+ NOT_IN_MAIN_DEX
+ }
+
+ // Specific set of classes specified to be in main-dex
+ private final Set<DexType> classList;
+ private final Map<DexType, DexType> synthesizedClassesMap;
+ // Traced roots are traced main dex references.
+ private final Set<DexType> tracedRoots;
+ // Traced method roots are the methods visited from an initial main dex root set. The set is
+ // cleared after the mergers has run.
+ private Set<DexMethod> tracedMethodRoots;
+ // Traced dependencies are those classes that are directly referenced from traced roots, but will
+ // not be loaded before loading the remaining dex files.
+ private final Set<DexType> tracedDependencies;
+ // Bit indicating if the traced methods are cleared.
+ private boolean tracedMethodRootsCleared = false;
+
+ private MainDexInfo(Set<DexType> classList) {
+ this(
+ classList,
+ Collections.emptySet(),
+ Collections.emptySet(),
+ Collections.emptySet(),
+ /* synthesized classes - cannot be emptyset */ Sets.newIdentityHashSet(),
+ false);
+ }
+
+ private MainDexInfo(
+ Set<DexType> classList,
+ Set<DexType> tracedRoots,
+ Set<DexMethod> tracedMethodRoots,
+ Set<DexType> tracedDependencies,
+ Set<DexType> synthesizedClasses,
+ boolean tracedMethodRootsCleared) {
+ this.classList = classList;
+ this.tracedRoots = tracedRoots;
+ this.tracedMethodRoots = tracedMethodRoots;
+ this.tracedDependencies = tracedDependencies;
+ this.synthesizedClassesMap = new ConcurrentHashMap<>();
+ this.tracedMethodRootsCleared = tracedMethodRootsCleared;
+ synthesizedClasses.forEach(type -> synthesizedClassesMap.put(type, type));
+ assert tracedDependencies.stream().noneMatch(tracedRoots::contains);
+ assert tracedRoots.containsAll(synthesizedClasses);
+ }
+
+ public boolean isNone() {
+ assert none() == NONE;
+ return this == NONE;
+ }
+
+ public boolean isMainDex(ProgramDefinition definition) {
+ return isFromList(definition) || isTracedRoot(definition) || isDependency(definition);
+ }
+
+ public boolean isFromList(ProgramDefinition definition) {
+ return isFromList(definition.getContextType());
+ }
+
+ private boolean isFromList(DexReference reference) {
+ return classList.contains(reference.getContextType());
+ }
+
+ public boolean isTracedRoot(ProgramDefinition definition) {
+ return isTracedRoot(definition.getContextType());
+ }
+
+ public boolean isTracedMethodRoot(DexMethod method) {
+ assert !tracedMethodRootsCleared : "Traced method roots are cleared after mergers has run";
+ return tracedMethodRoots.contains(method);
+ }
+
+ private boolean isTracedRoot(DexReference reference) {
+ return tracedRoots.contains(reference.getContextType());
+ }
+
+ private boolean isDependency(ProgramDefinition definition) {
+ return isDependency(definition.getContextType());
+ }
+
+ private boolean isDependency(DexReference reference) {
+ return tracedDependencies.contains(reference.getContextType());
+ }
+
+ public boolean isTracedMethodRootsCleared() {
+ return tracedMethodRootsCleared;
+ }
+
+ public void clearTracedMethodRoots() {
+ this.tracedMethodRootsCleared = true;
+ this.tracedMethodRoots = Sets.newIdentityHashSet();
+ }
+
+ public boolean canRebindReference(ProgramMethod context, DexReference referenceToTarget) {
+ MainDexGroup holderGroup = getMainDexGroupInternal(context);
+ if (holderGroup == MainDexGroup.NOT_IN_MAIN_DEX
+ || holderGroup == MainDexGroup.MAIN_DEX_DEPENDENCY) {
+ // We are always free to rebind/inline into something not in main-dex or traced dependencies.
+ return true;
+ }
+ if (holderGroup == MainDexGroup.MAIN_DEX_LIST) {
+ // If the holder is in the class list, we are not allowed to make any assumptions on the
+ // holder being a root or a dependency. Therefore we cannot merge.
+ return false;
+ }
+ assert holderGroup == MAIN_DEX_ROOT;
+ // Otherwise we allow if either is both root.
+ return getMainDexGroupInternal(referenceToTarget) == MAIN_DEX_ROOT;
+ }
+
+ public boolean canMerge(ProgramDefinition candidate) {
+ return !isFromList(candidate);
+ }
+
+ public boolean canMerge(ProgramDefinition source, ProgramDefinition target) {
+ return canMerge(source.getContextType(), target.getContextType());
+ }
+
+ private boolean canMerge(DexReference source, DexReference target) {
+ MainDexGroup sourceGroup = getMainDexGroupInternal(source);
+ MainDexGroup targetGroup = getMainDexGroupInternal(target);
+ if (sourceGroup != targetGroup) {
+ return false;
+ }
+ // If the holder is in the class list, we are not allowed to make any assumptions on the holder
+ // being a root or a dependency. Therefore we cannot merge.
+ return sourceGroup != MainDexGroup.MAIN_DEX_LIST;
+ }
+
+ public MainDexGroup getMergeKey(ProgramDefinition mergeCandidate) {
+ MainDexGroup mainDexGroupInternal = getMainDexGroupInternal(mergeCandidate);
+ return mainDexGroupInternal == MainDexGroup.MAIN_DEX_LIST ? null : mainDexGroupInternal;
+ }
+
+ private MainDexGroup getMainDexGroupInternal(ProgramDefinition definition) {
+ return getMainDexGroupInternal(definition.getReference());
+ }
+
+ private MainDexGroup getMainDexGroupInternal(DexReference reference) {
+ if (isFromList(reference)) {
+ return MainDexGroup.MAIN_DEX_LIST;
+ }
+ if (isTracedRoot(reference)) {
+ return MAIN_DEX_ROOT;
+ }
+ if (isDependency(reference)) {
+ return MainDexGroup.MAIN_DEX_DEPENDENCY;
+ }
+ return MainDexGroup.NOT_IN_MAIN_DEX;
+ }
+
+ public boolean disallowInliningIntoContext(
+ AppInfoWithClassHierarchy appInfo, ProgramDefinition context, ProgramMethod method) {
+ if (context.getContextType() == method.getContextType()) {
+ return false;
+ }
+ MainDexGroup mainDexGroupInternal = getMainDexGroupInternal(context);
+ if (mainDexGroupInternal == MainDexGroup.NOT_IN_MAIN_DEX
+ || mainDexGroupInternal == MainDexGroup.MAIN_DEX_DEPENDENCY) {
+ return false;
+ }
+ if (mainDexGroupInternal == MainDexGroup.MAIN_DEX_LIST) {
+ return MainDexDirectReferenceTracer.hasReferencesOutsideMainDexClasses(
+ appInfo, method, not(this::isFromList));
+ }
+ assert mainDexGroupInternal == MAIN_DEX_ROOT;
+ return MainDexDirectReferenceTracer.hasReferencesOutsideMainDexClasses(
+ appInfo, method, not(this::isTracedRoot));
+ }
+
+ public boolean isEmpty() {
+ assert !tracedRoots.isEmpty() || tracedDependencies.isEmpty();
+ return tracedRoots.isEmpty() && classList.isEmpty();
+ }
+
+ public static MainDexInfo none() {
+ return NONE;
+ }
+
+ // TODO(b/178127572): This mutates the MainDexClasses which otherwise should be immutable.
+ public void addSyntheticClass(DexProgramClass clazz) {
+ // TODO(b/178127572): This will add a synthesized type as long as the initial set is not empty.
+ // A better approach would be to use the context for the synthetic with a containment check.
+ assert !isNone();
+ if (!classList.isEmpty()) {
+ synthesizedClassesMap.computeIfAbsent(
+ clazz.type,
+ type -> {
+ classList.add(type);
+ return type;
+ });
+ }
+ if (!tracedRoots.isEmpty()) {
+ synthesizedClassesMap.computeIfAbsent(
+ clazz.type,
+ type -> {
+ tracedRoots.add(type);
+ return type;
+ });
+ }
+ }
+
+ public void addLegacySyntheticClass(DexProgramClass clazz, ProgramDefinition context) {
+ if (isTracedRoot(context) || isFromList(context) || isDependency(context)) {
+ addSyntheticClass(clazz);
+ }
+ }
+
+ public int size() {
+ return classList.size() + tracedRoots.size() + tracedDependencies.size();
+ }
+
+ public void forEachExcludingDependencies(Consumer<DexType> fn) {
+ // Prevent seeing duplicates in the list and roots.
+ Set<DexType> seen = Sets.newIdentityHashSet();
+ classList.forEach(ConsumerUtils.acceptIfNotSeen(fn, seen));
+ tracedRoots.forEach(ConsumerUtils.acceptIfNotSeen(fn, seen));
+ }
+
+ public void forEach(Consumer<DexType> fn) {
+ // Prevent seeing duplicates in the list and roots.
+ Set<DexType> seen = Sets.newIdentityHashSet();
+ classList.forEach(ConsumerUtils.acceptIfNotSeen(fn, seen));
+ tracedRoots.forEach(ConsumerUtils.acceptIfNotSeen(fn, seen));
+ tracedDependencies.forEach(ConsumerUtils.acceptIfNotSeen(fn, seen));
+ }
+
+ public MainDexInfo withoutPrunedItems(PrunedItems prunedItems) {
+ if (prunedItems.isEmpty()) {
+ return this;
+ }
+ Set<DexType> removedClasses = prunedItems.getRemovedClasses();
+ Set<DexType> modifiedClassList = Sets.newIdentityHashSet();
+ Set<DexType> modifiedSynthesized = Sets.newIdentityHashSet();
+ classList.forEach(type -> ifNotRemoved(type, removedClasses, modifiedClassList::add));
+ synthesizedClassesMap
+ .keySet()
+ .forEach(type -> ifNotRemoved(type, removedClasses, modifiedSynthesized::add));
+ MainDexInfo.Builder builder = builder();
+ tracedRoots.forEach(type -> ifNotRemoved(type, removedClasses, builder::addRoot));
+ // TODO(b/169927809): Methods could be pruned without the holder being pruned, however, one has
+ // to have a reference for querying a root.
+ tracedMethodRoots.forEach(
+ method ->
+ ifNotRemoved(
+ method.getHolderType(), removedClasses, ignored -> builder.addRoot(method)));
+ tracedDependencies.forEach(type -> ifNotRemoved(type, removedClasses, builder::addDependency));
+ return builder.build(modifiedClassList, modifiedSynthesized);
+ }
+
+ private void ifNotRemoved(
+ DexType type, Set<DexType> removedClasses, Consumer<DexType> notRemoved) {
+ if (!removedClasses.contains(type)) {
+ notRemoved.accept(type);
+ }
+ }
+
+ public MainDexInfo rewrittenWithLens(GraphLens lens) {
+ Set<DexType> modifiedClassList = Sets.newIdentityHashSet();
+ Set<DexType> modifiedSynthesized = Sets.newIdentityHashSet();
+ classList.forEach(
+ type -> rewriteAndApplyIfNotPrimitiveType(lens, type, modifiedClassList::add));
+ synthesizedClassesMap.keySet().forEach(type -> modifiedSynthesized.add(lens.lookupType(type)));
+ MainDexInfo.Builder builder = builder();
+ tracedRoots.forEach(type -> rewriteAndApplyIfNotPrimitiveType(lens, type, builder::addRoot));
+ tracedMethodRoots.forEach(method -> builder.addRoot(lens.getRenamedMethodSignature(method)));
+ tracedDependencies.forEach(
+ type -> {
+ if (lens.isSyntheticFinalizationGraphLens()) {
+ // Synthetic finalization is allowed to merge identical classes into the same class. The
+ // rewritten type of a traced dependency can therefore be finalized with a traced root.
+ rewriteAndApplyIfNotPrimitiveType(lens, type, builder::addDependencyIfNotRoot);
+ } else {
+ rewriteAndApplyIfNotPrimitiveType(lens, type, builder::addDependency);
+ }
+ });
+ return builder.build(modifiedClassList, modifiedSynthesized);
+ }
+
+ public Builder builder() {
+ return new Builder(tracedMethodRootsCleared);
+ }
+
+ public static class Builder {
+
+ private final Set<DexType> list = Sets.newIdentityHashSet();
+ private final Set<DexType> roots = Sets.newIdentityHashSet();
+ private final Set<DexMethod> methodRoots = Sets.newIdentityHashSet();
+ private final Set<DexType> dependencies = Sets.newIdentityHashSet();
+ private final boolean tracedMethodRootsCleared;
+
+ private Builder(boolean tracedMethodRootsCleared) {
+ this.tracedMethodRootsCleared = tracedMethodRootsCleared;
+ }
+
+ public void addList(DexProgramClass clazz) {
+ addList(clazz.getType());
+ }
+
+ public void addList(DexType type) {
+ list.add(type);
+ }
+
+ public void addRoot(DexProgramClass clazz) {
+ addRoot(clazz.getType());
+ }
+
+ public void addRoot(DexType type) {
+ assert !dependencies.contains(type);
+ roots.add(type);
+ }
+
+ public void addRoot(DexMethod method) {
+ methodRoots.add(method);
+ }
+
+ public void addDependency(DexProgramClass clazz) {
+ addDependency(clazz.getType());
+ }
+
+ public void addDependency(DexType type) {
+ assert !roots.contains(type);
+ dependencies.add(type);
+ }
+
+ public void addDependencyIfNotRoot(DexType type) {
+ if (roots.contains(type)) {
+ return;
+ }
+ addDependency(type);
+ }
+
+ public boolean isTracedRoot(DexProgramClass clazz) {
+ return isTracedRoot(clazz.getType());
+ }
+
+ public boolean isTracedRoot(DexType type) {
+ return roots.contains(type);
+ }
+
+ public boolean isDependency(DexProgramClass clazz) {
+ return isDependency(clazz.getType());
+ }
+
+ public boolean isDependency(DexType type) {
+ return dependencies.contains(type);
+ }
+
+ public boolean contains(DexProgramClass clazz) {
+ return contains(clazz.type);
+ }
+
+ public boolean contains(DexType type) {
+ return isTracedRoot(type) || isDependency(type);
+ }
+
+ public Set<DexType> getRoots() {
+ return roots;
+ }
+
+ public MainDexInfo buildList() {
+ // When building without passing the list, the method roots and dependencies should
+ // be empty since no tracing has been done.
+ assert dependencies.isEmpty();
+ assert roots.isEmpty();
+ return new MainDexInfo(list);
+ }
+
+ public MainDexInfo build(Set<DexType> classList, Set<DexType> synthesizedClasses) {
+ // Class can contain dependencies which we should not regard as roots.
+ assert list.isEmpty();
+ return new MainDexInfo(
+ classList,
+ SetUtils.unionIdentityHashSet(roots, synthesizedClasses),
+ methodRoots,
+ Sets.difference(dependencies, synthesizedClasses),
+ synthesizedClasses,
+ tracedMethodRootsCleared);
+ }
+
+ public MainDexInfo build(MainDexInfo previous) {
+ return build(previous.classList, previous.synthesizedClassesMap.keySet());
+ }
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/shaking/MainDexListBuilder.java b/src/main/java/com/android/tools/r8/shaking/MainDexListBuilder.java
index 4fa5d3a..afb39bf 100644
--- a/src/main/java/com/android/tools/r8/shaking/MainDexListBuilder.java
+++ b/src/main/java/com/android/tools/r8/shaking/MainDexListBuilder.java
@@ -13,7 +13,6 @@
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.DexProto;
import com.android.tools.r8.graph.DexType;
-import com.android.tools.r8.utils.SetUtils;
import java.util.IdentityHashMap;
import java.util.Map;
import java.util.Set;
@@ -30,7 +29,7 @@
private final Set<DexType> roots;
private final AppView<? extends AppInfoWithClassHierarchy> appView;
private final Map<DexType, Boolean> annotationTypeContainEnum;
- private final MainDexTracingResult.Builder mainDexClassesBuilder;
+ private final MainDexInfo.Builder mainDexInfoBuilder;
public static void checkForAssumedLibraryTypes(AppInfo appInfo) {
DexClass enumType = appInfo.definitionFor(appInfo.dexItemFactory().enumType);
@@ -50,11 +49,14 @@
* @param appView the dex appplication.
*/
public MainDexListBuilder(
- Set<DexProgramClass> roots, AppView<? extends AppInfoWithClassHierarchy> appView) {
+ AppView<? extends AppInfoWithClassHierarchy> appView,
+ Set<DexType> roots,
+ MainDexInfo.Builder mainDexInfoBuilder) {
this.appView = appView;
// Only consider program classes for the root set.
- this.roots = SetUtils.mapIdentityHashSet(roots, DexProgramClass::getType);
- mainDexClassesBuilder = MainDexTracingResult.builder(appView.appInfo()).addRoots(this.roots);
+ assert roots.stream().allMatch(type -> appView.definitionFor(type).isProgramClass());
+ this.roots = roots;
+ this.mainDexInfoBuilder = mainDexInfoBuilder;
annotationTypeContainEnum = new IdentityHashMap<>();
}
@@ -62,18 +64,17 @@
return appView.appInfo();
}
- public MainDexTracingResult run() {
+ public void run() {
traceMainDexDirectDependencies();
traceRuntimeAnnotationsWithEnumForMainDex();
- return mainDexClassesBuilder.build();
}
private void traceRuntimeAnnotationsWithEnumForMainDex() {
for (DexProgramClass clazz : appInfo().classes()) {
- DexType dexType = clazz.type;
- if (mainDexClassesBuilder.contains(dexType)) {
+ if (mainDexInfoBuilder.contains(clazz)) {
continue;
}
+ DexType dexType = clazz.type;
if (isAnnotation(dexType) && isAnnotationWithEnum(dexType)) {
addAnnotationsWithEnum(clazz);
continue;
@@ -82,10 +83,11 @@
// annotations with enums goes into the main dex, move annotated classes there as well.
clazz.forEachAnnotation(
annotation -> {
- if (!mainDexClassesBuilder.contains(dexType)
+ if (!mainDexInfoBuilder.contains(clazz)
&& annotation.visibility == DexAnnotation.VISIBILITY_RUNTIME
&& isAnnotationWithEnum(annotation.annotation.type)) {
- addClassAnnotatedWithAnnotationWithEnum(dexType);
+ // Just add classes annotated with annotations with enum as direct dependencies.
+ mainDexInfoBuilder.addDependency(clazz);
}
});
}
@@ -151,16 +153,10 @@
}
}
- private void addClassAnnotatedWithAnnotationWithEnum(DexType type) {
- // Just add classes annotated with annotations with enum ad direct dependencies.
- addDirectDependency(type);
- }
-
private void addDirectDependency(DexType type) {
// Consider only component type of arrays
type = type.toBaseType(appView.dexItemFactory());
-
- if (!type.isClassType() || mainDexClassesBuilder.contains(type)) {
+ if (!type.isClassType() || mainDexInfoBuilder.contains(type)) {
return;
}
@@ -173,9 +169,8 @@
}
private void addDirectDependency(DexProgramClass dexClass) {
- DexType type = dexClass.type;
- assert !mainDexClassesBuilder.contains(type);
- mainDexClassesBuilder.addDependency(type);
+ assert !mainDexInfoBuilder.contains(dexClass);
+ mainDexInfoBuilder.addDependency(dexClass);
if (dexClass.superType != null) {
addDirectDependency(dexClass.superType);
}
diff --git a/src/main/java/com/android/tools/r8/shaking/MainDexTracingResult.java b/src/main/java/com/android/tools/r8/shaking/MainDexTracingResult.java
deleted file mode 100644
index 5bf287f..0000000
--- a/src/main/java/com/android/tools/r8/shaking/MainDexTracingResult.java
+++ /dev/null
@@ -1,172 +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.shaking;
-
-import com.android.tools.r8.graph.AppInfo;
-import com.android.tools.r8.graph.DexClass;
-import com.android.tools.r8.graph.DexProgramClass;
-import com.android.tools.r8.graph.DexType;
-import com.android.tools.r8.graph.ProgramDefinition;
-import com.android.tools.r8.utils.SetUtils;
-import com.google.common.collect.ImmutableSet;
-import com.google.common.collect.Sets;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.Set;
-import java.util.function.Consumer;
-import java.util.function.Predicate;
-
-public class MainDexTracingResult {
-
- public static MainDexTracingResult NONE =
- new MainDexTracingResult(ImmutableSet.of(), ImmutableSet.of());
-
- public static class Builder {
- public final AppInfo appInfo;
- public final Set<DexType> roots;
- public final Set<DexType> dependencies;
-
- private Builder(AppInfo appInfo) {
- this(appInfo, Sets.newIdentityHashSet(), Sets.newIdentityHashSet());
- }
-
- private Builder(AppInfo appInfo, MainDexTracingResult mainDexTracingResult) {
- this(
- appInfo,
- SetUtils.newIdentityHashSet(mainDexTracingResult.getRoots()),
- SetUtils.newIdentityHashSet(mainDexTracingResult.getDependencies()));
- }
-
- private Builder(AppInfo appInfo, Set<DexType> roots, Set<DexType> dependencies) {
- this.appInfo = appInfo;
- this.roots = roots;
- this.dependencies = dependencies;
- }
-
- public Builder addRoot(DexProgramClass clazz) {
- roots.add(clazz.getType());
- return this;
- }
-
- public Builder addRoot(DexType type) {
- assert isProgramClass(type) : type.toSourceString();
- roots.add(type);
- return this;
- }
-
- public Builder addRoots(Collection<DexType> rootSet) {
- assert rootSet.stream().allMatch(this::isProgramClass);
- this.roots.addAll(rootSet);
- return this;
- }
-
- public Builder addDependency(DexType type) {
- assert isProgramClass(type);
- dependencies.add(type);
- return this;
- }
-
- public boolean contains(DexType type) {
- return roots.contains(type) || dependencies.contains(type);
- }
-
- public MainDexTracingResult build() {
- return new MainDexTracingResult(roots, dependencies);
- }
-
- private boolean isProgramClass(DexType dexType) {
- DexClass clazz = appInfo.definitionFor(dexType);
- return clazz != null && clazz.isProgramClass();
- }
- }
-
- // The classes in the root set.
- private final Set<DexType> roots;
- // Additional dependencies (direct dependencies and runtime annotations with enums).
- private final Set<DexType> dependencies;
- // All main dex classes.
- private final Set<DexType> classes;
-
- private MainDexTracingResult(Set<DexType> roots, Set<DexType> dependencies) {
- assert Sets.intersection(roots, dependencies).isEmpty();
- this.roots = Collections.unmodifiableSet(roots);
- this.dependencies = Collections.unmodifiableSet(dependencies);
- this.classes = Sets.union(roots, dependencies);
- }
-
- public boolean canReferenceItemFromContextWithoutIncreasingMainDexSize(
- ProgramDefinition item, ProgramDefinition context) {
- // If the context is not a root, then additional references from inside the context will not
- // increase the size of the main dex.
- if (!isRoot(context)) {
- return true;
- }
- // Otherwise, require that the item is a root itself.
- return isRoot(item);
- }
-
- public boolean isEmpty() {
- assert !roots.isEmpty() || dependencies.isEmpty();
- return roots.isEmpty();
- }
-
- public Set<DexType> getRoots() {
- return roots;
- }
-
- public Set<DexType> getDependencies() {
- return dependencies;
- }
-
- public Set<DexType> getClasses() {
- return classes;
- }
-
- public boolean contains(ProgramDefinition clazz) {
- return contains(clazz.getContextType());
- }
-
- public boolean contains(DexType type) {
- return getClasses().contains(type);
- }
-
- private void collectTypesMatching(
- Set<DexType> types, Predicate<DexType> predicate, Consumer<DexType> consumer) {
- types.forEach(
- type -> {
- if (predicate.test(type)) {
- consumer.accept(type);
- }
- });
- }
-
- public boolean isRoot(ProgramDefinition definition) {
- return getRoots().contains(definition.getContextType());
- }
-
- public boolean isRoot(DexType type) {
- return getRoots().contains(type);
- }
-
- public boolean isDependency(ProgramDefinition definition) {
- return getDependencies().contains(definition.getContextType());
- }
-
- public MainDexTracingResult prunedCopy(AppInfoWithLiveness appInfo) {
- Builder builder = builder(appInfo);
- Predicate<DexType> wasPruned = appInfo::wasPruned;
- collectTypesMatching(roots, wasPruned.negate(), builder::addRoot);
- collectTypesMatching(dependencies, wasPruned.negate(), builder::addDependency);
- return builder.build();
- }
-
- public static Builder builder(AppInfo appInfo) {
- return new Builder(appInfo);
- }
-
- public Builder extensionBuilder(AppInfo appInfo) {
- return new Builder(appInfo, this);
- }
-}
diff --git a/src/main/java/com/android/tools/r8/shaking/MissingClasses.java b/src/main/java/com/android/tools/r8/shaking/MissingClasses.java
index e296cd7..dee668f 100644
--- a/src/main/java/com/android/tools/r8/shaking/MissingClasses.java
+++ b/src/main/java/com/android/tools/r8/shaking/MissingClasses.java
@@ -4,15 +4,27 @@
package com.android.tools.r8.shaking;
+import static com.android.tools.r8.ir.desugar.DesugaredLibraryAPIConverter.DESCRIPTOR_VIVIFIED_PREFIX;
+import static com.android.tools.r8.ir.desugar.DesugaredLibraryRetargeter.getRetargetPackageAndClassPrefixDescriptor;
+import static com.android.tools.r8.ir.desugar.InterfaceMethodRewriter.EMULATE_LIBRARY_CLASS_NAME_SUFFIX;
+
+import com.android.tools.r8.errors.dontwarn.DontWarnConfiguration;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.graph.DexReference;
+import com.android.tools.r8.graph.DexString;
import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.ProgramDerivedContext;
import com.android.tools.r8.synthesis.CommittedItems;
import com.android.tools.r8.utils.InternalOptions;
-import com.google.common.collect.ImmutableList;
+import com.android.tools.r8.utils.SetUtils;
+import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Sets;
import java.util.Collection;
+import java.util.IdentityHashMap;
+import java.util.Map;
import java.util.Set;
+import java.util.function.Predicate;
public class MissingClasses {
@@ -48,7 +60,7 @@
public static class Builder {
private final Set<DexType> alreadyMissingClasses;
- private final Set<DexType> newMissingClasses = Sets.newIdentityHashSet();
+ private final Map<DexType, Set<DexReference>> newMissingClasses = new IdentityHashMap<>();
// Set of missing types that are not to be reported as missing. This does not hide reports
// if the same type is in newMissingClasses in which case it is reported regardless.
@@ -62,12 +74,27 @@
this.alreadyMissingClasses = alreadyMissingClasses;
}
- public void addNewMissingClass(DexType type) {
- newMissingClasses.add(type);
+ public void addNewMissingClass(DexType type, ProgramDerivedContext context) {
+ assert context != null;
+ assert context.getContext().getContextType() != type;
+ if (!alreadyMissingClasses.contains(type)) {
+ newMissingClasses
+ .computeIfAbsent(type, ignore -> Sets.newIdentityHashSet())
+ .add(context.getContext().getReference());
+ }
}
- public Builder addNewMissingClasses(Collection<DexType> types) {
- newMissingClasses.addAll(types);
+ public void legacyAddNewMissingClass(DexType type) {
+ if (!alreadyMissingClasses.contains(type)) {
+ // The legacy reporting is context insensitive, so therefore we use the missing classes
+ // themselves as contexts.
+ newMissingClasses.computeIfAbsent(type, ignore -> Sets.newIdentityHashSet()).add(type);
+ }
+ }
+
+ @Deprecated
+ public Builder legacyAddNewMissingClasses(Collection<DexType> types) {
+ types.forEach(this::legacyAddNewMissingClass);
return this;
}
@@ -76,7 +103,7 @@
}
public boolean contains(DexType type) {
- return alreadyMissingClasses.contains(type) || newMissingClasses.contains(type);
+ return alreadyMissingClasses.contains(type) || newMissingClasses.containsKey(type);
}
Builder removeAlreadyMissingClasses(Iterable<DexType> types) {
@@ -93,15 +120,12 @@
public MissingClasses reportMissingClasses(AppView<?> appView) {
InternalOptions options = appView.options();
- Set<DexType> newMissingClassesWithoutDontWarn =
- appView.getDontWarnConfiguration().getNonMatches(newMissingClasses);
- newMissingClassesWithoutDontWarn.removeAll(alreadyMissingClasses);
- newMissingClassesWithoutDontWarn.removeAll(
- getAllowedMissingClasses(appView.dexItemFactory()));
- if (!newMissingClassesWithoutDontWarn.isEmpty()) {
+ Map<DexType, Set<DexReference>> missingClassesToBeReported =
+ getMissingClassesToBeReported(appView);
+ if (!missingClassesToBeReported.isEmpty()) {
MissingClassesDiagnostic diagnostic =
new MissingClassesDiagnostic.Builder()
- .addMissingClasses(newMissingClassesWithoutDontWarn)
+ .addMissingClasses(missingClassesToBeReported)
.setFatal(!options.ignoreMissingClasses)
.build();
if (options.ignoreMissingClasses) {
@@ -113,21 +137,83 @@
return build();
}
- private static Collection<DexType> getAllowedMissingClasses(DexItemFactory dexItemFactory) {
- return ImmutableList.of(
- dexItemFactory.annotationDefault,
- dexItemFactory.annotationMethodParameters,
- dexItemFactory.annotationSourceDebugExtension,
- dexItemFactory.annotationThrows,
- // TODO(b/176133674) StringConcatFactory is backported, but the class is reported as
- // missing because the enqueuer runs prior to backporting and thus sees the non-desugared
- // code.
- dexItemFactory.stringConcatFactoryType);
+ private Map<DexType, Set<DexReference>> getMissingClassesToBeReported(AppView<?> appView) {
+ Predicate<DexType> allowedMissingClassesPredicate =
+ getIsAllowedMissingClassesPredicate(appView);
+ Map<DexType, Set<DexReference>> missingClassesToBeReported =
+ new IdentityHashMap<>(newMissingClasses.size());
+ newMissingClasses.forEach(
+ (missingClass, contexts) -> {
+ // Don't report "allowed" missing classes (e.g., classes matched by -dontwarn).
+ if (allowedMissingClassesPredicate.test(missingClass)) {
+ return;
+ }
+
+ // Remove all contexts that are matched by a -dontwarn rule (a missing class should not
+ // be reported if it os only referenced from contexts that are matched by a -dontwarn).
+ contexts.removeIf(
+ context -> appView.getDontWarnConfiguration().matches(context.getContextType()));
+
+ // If there are any contexts not matched by a -dontwarn rule, then report.
+ if (!contexts.isEmpty()) {
+ missingClassesToBeReported.put(missingClass, contexts);
+ }
+ });
+ return missingClassesToBeReported;
}
+ private static Predicate<DexType> getIsAllowedMissingClassesPredicate(AppView<?> appView) {
+ Set<DexType> allowedMissingClasses = getAllowedMissingClasses(appView.dexItemFactory());
+ Predicate<DexType> compilerSynthesizedAllowingMissingClassPredicate =
+ getIsCompilerSynthesizedAllowedMissingClassesPredicate(appView);
+ DontWarnConfiguration dontWarnConfiguration = appView.getDontWarnConfiguration();
+ return type ->
+ allowedMissingClasses.contains(type)
+ || compilerSynthesizedAllowingMissingClassPredicate.test(type)
+ || dontWarnConfiguration.matches(type);
+ }
+
+ private static Set<DexType> getAllowedMissingClasses(DexItemFactory dexItemFactory) {
+ return ImmutableSet.<DexType>builder()
+ .add(
+ dexItemFactory.annotationDefault,
+ dexItemFactory.annotationMethodParameters,
+ dexItemFactory.annotationSourceDebugExtension,
+ dexItemFactory.annotationSynthesizedClass,
+ dexItemFactory.annotationSynthesizedClassMap,
+ dexItemFactory.annotationThrows,
+ dexItemFactory.serializedLambdaType,
+ // TODO(b/176133674) StringConcatFactory is backported, but the class is reported as
+ // missing because the enqueuer runs prior to backporting and thus sees the
+ // non-desugared code.
+ dexItemFactory.stringConcatFactoryType)
+ .addAll(dexItemFactory.getConversionTypes())
+ .build();
+ }
+
+ private static Predicate<DexType> getIsCompilerSynthesizedAllowedMissingClassesPredicate(
+ AppView<?> appView) {
+ DexItemFactory dexItemFactory = appView.dexItemFactory();
+ InternalOptions options = appView.options();
+ DexString emulatedLibraryClassNameSuffix =
+ dexItemFactory.createString(EMULATE_LIBRARY_CLASS_NAME_SUFFIX + ";");
+ DexString retargetPackageAndClassPrefixDescriptor =
+ dexItemFactory.createString(
+ getRetargetPackageAndClassPrefixDescriptor(options.desugaredLibraryConfiguration));
+ DexString vivifiedClassNamePrefix = dexItemFactory.createString(DESCRIPTOR_VIVIFIED_PREFIX);
+ return type -> {
+ DexString descriptor = type.getDescriptor();
+ return descriptor.startsWith(retargetPackageAndClassPrefixDescriptor)
+ || descriptor.startsWith(vivifiedClassNamePrefix)
+ || descriptor.endsWith(emulatedLibraryClassNameSuffix);
+ };
+ }
+
+
+
/** Intentionally private, use {@link Builder#reportMissingClasses(AppView)}. */
private MissingClasses build() {
- // Extend the newMissingClasses set with all other missing classes.
+ // Return the new set of missing classes.
//
// We also add newIgnoredMissingClasses to newMissingClasses to be able to assert that we have
// a closed world after the first round of tree shaking: we should never lookup a class that
@@ -137,9 +223,9 @@
// Note: At this point, all missing classes in newMissingClasses have already been reported.
// Thus adding newIgnoredMissingClasses to newMissingClasses will not lead to reports for the
// classes in newIgnoredMissingClasses.
- newMissingClasses.addAll(alreadyMissingClasses);
- newMissingClasses.addAll(newIgnoredMissingClasses);
- return new MissingClasses(newMissingClasses);
+ return new MissingClasses(
+ SetUtils.newIdentityHashSet(
+ alreadyMissingClasses, newMissingClasses.keySet(), newIgnoredMissingClasses));
}
public boolean wasAlreadyMissing(DexType type) {
diff --git a/src/main/java/com/android/tools/r8/shaking/MissingClassesDiagnostic.java b/src/main/java/com/android/tools/r8/shaking/MissingClassesDiagnostic.java
index 5965cc5..0f4e97b 100644
--- a/src/main/java/com/android/tools/r8/shaking/MissingClassesDiagnostic.java
+++ b/src/main/java/com/android/tools/r8/shaking/MissingClassesDiagnostic.java
@@ -4,34 +4,130 @@
package com.android.tools.r8.shaking;
+import static com.android.tools.r8.utils.PredicateUtils.not;
+
import com.android.tools.r8.Diagnostic;
import com.android.tools.r8.Keep;
+import com.android.tools.r8.graph.DexField;
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexReference;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.origin.Origin;
import com.android.tools.r8.position.Position;
import com.android.tools.r8.references.ClassReference;
+import com.android.tools.r8.references.FieldReference;
+import com.android.tools.r8.references.MethodReference;
import com.android.tools.r8.references.Reference;
-import com.google.common.collect.ImmutableSortedSet;
-import java.util.Collection;
+import com.android.tools.r8.utils.FieldReferenceUtils;
+import com.android.tools.r8.utils.MethodReferenceUtils;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.ImmutableSortedMap;
+import com.google.common.collect.Sets;
+import java.util.ArrayList;
+import java.util.Collections;
import java.util.Comparator;
import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Optional;
import java.util.Set;
-import java.util.SortedSet;
+import java.util.SortedMap;
+import java.util.function.Function;
@Keep
public class MissingClassesDiagnostic implements Diagnostic {
- private final boolean fatal;
- private final SortedSet<ClassReference> missingClasses;
+ private static class MissingClassAccessContexts {
- private MissingClassesDiagnostic(boolean fatal, SortedSet<ClassReference> missingClasses) {
+ private ImmutableSet<ClassReference> classContexts;
+ private ImmutableSet<FieldReference> fieldContexts;
+ private ImmutableSet<MethodReference> methodContexts;
+
+ private MissingClassAccessContexts(
+ ImmutableSet<ClassReference> classContexts,
+ ImmutableSet<FieldReference> fieldContexts,
+ ImmutableSet<MethodReference> methodContexts) {
+ this.classContexts = classContexts;
+ this.fieldContexts = fieldContexts;
+ this.methodContexts = methodContexts;
+ }
+
+ static Builder builder() {
+ return new Builder();
+ }
+
+ String getReferencedFromMessageSuffix(ClassReference missingClass) {
+ if (!fieldContexts.isEmpty()) {
+ return " (referenced from: "
+ + FieldReferenceUtils.toSourceString(fieldContexts.iterator().next())
+ + ")";
+ }
+ if (!methodContexts.isEmpty()) {
+ return " (referenced from: "
+ + MethodReferenceUtils.toSourceString(methodContexts.iterator().next())
+ + ")";
+ }
+ // TODO(b/175543745): The legacy reporting is context insensitive, and therefore uses the
+ // missing classes as their own context. Once legacy reporting is removed, this should be
+ // simplified to taking the first context.
+ Optional<ClassReference> classContext =
+ classContexts.stream().filter(not(missingClass::equals)).findFirst();
+ return classContext
+ .map(classReference -> " (referenced from: " + classReference.getTypeName() + ")")
+ .orElse("");
+ }
+
+ static class Builder {
+
+ private final Set<DexReference> contexts = Sets.newIdentityHashSet();
+
+ Builder addAll(Set<DexReference> contexts) {
+ this.contexts.addAll(contexts);
+ return this;
+ }
+
+ // TODO(b/179249745): Sort on demand in getReferencedFromMessageSuffix() instead.
+ MissingClassAccessContexts build() {
+ // Sort the contexts for deterministic reporting.
+ List<DexType> classContexts = new ArrayList<>();
+ List<DexField> fieldContexts = new ArrayList<>();
+ List<DexMethod> methodContexts = new ArrayList<>();
+ contexts.forEach(
+ context -> context.apply(classContexts::add, fieldContexts::add, methodContexts::add));
+ Collections.sort(classContexts);
+ Collections.sort(fieldContexts);
+ Collections.sort(methodContexts);
+
+ // Build immutable sets (which preserve insertion order) from the sorted lists, mapping each
+ // DexType, DexField, and DexMethod to ClassReference, FieldReference, and MethodReference,
+ // respectively.
+ return new MissingClassAccessContexts(
+ toImmutableSet(classContexts, DexType::asClassReference),
+ toImmutableSet(fieldContexts, DexField::asFieldReference),
+ toImmutableSet(methodContexts, DexMethod::asMethodReference));
+ }
+
+ private <S, T> ImmutableSet<T> toImmutableSet(List<S> list, Function<S, T> fn) {
+ ImmutableSet.Builder<T> builder = ImmutableSet.builder();
+ list.forEach(element -> builder.add(fn.apply(element)));
+ return builder.build();
+ }
+ }
+ }
+
+ private final boolean fatal;
+ private final SortedMap<ClassReference, MissingClassAccessContexts> missingClasses;
+
+ private MissingClassesDiagnostic(
+ boolean fatal, SortedMap<ClassReference, MissingClassAccessContexts> missingClasses) {
assert !missingClasses.isEmpty();
this.fatal = fatal;
this.missingClasses = missingClasses;
}
public Set<ClassReference> getMissingClasses() {
- return missingClasses;
+ return missingClasses.keySet();
}
/** A missing class(es) failure can generally not be attributed to a single origin. */
@@ -46,7 +142,6 @@
return Position.UNKNOWN;
}
- // TODO(b/175755807): Extend diagnostic message with contextual information.
@Override
public String getDiagnosticMessage() {
return fatal ? getFatalDiagnosticMessage() : getNonFatalDiagnosticMessage();
@@ -54,43 +149,70 @@
private String getFatalDiagnosticMessage() {
if (missingClasses.size() == 1) {
- return "Compilation can't be completed because the class "
- + missingClasses.iterator().next().getTypeName()
- + " is missing.";
+ StringBuilder builder =
+ new StringBuilder(
+ "Compilation can't be completed because the following class is missing: ");
+ writeMissingClass(builder, missingClasses.entrySet().iterator().next());
+ return builder.append(".").toString();
}
+
StringBuilder builder =
new StringBuilder("Compilation can't be completed because the following ")
.append(missingClasses.size())
.append(" classes are missing:");
- for (ClassReference missingClass : missingClasses) {
- builder.append(System.lineSeparator()).append("- ").append(missingClass.getTypeName());
- }
+ missingClasses.forEach(
+ (missingClass, contexts) ->
+ writeMissingClass(
+ builder.append(System.lineSeparator()).append("- "), missingClass, contexts));
return builder.toString();
}
private String getNonFatalDiagnosticMessage() {
StringBuilder builder = new StringBuilder();
- Iterator<ClassReference> missingClassesIterator = missingClasses.iterator();
- while (missingClassesIterator.hasNext()) {
- ClassReference missingClass = missingClassesIterator.next();
- builder.append("Missing class ").append(missingClass.getTypeName());
- if (missingClassesIterator.hasNext()) {
- builder.append(System.lineSeparator());
- }
- }
+ Iterator<Entry<ClassReference, MissingClassAccessContexts>> missingClassesIterator =
+ missingClasses.entrySet().iterator();
+
+ // The diagnostic is always non-empty.
+ assert missingClassesIterator.hasNext();
+
+ // Write first line.
+ writeMissingClass(builder.append("Missing class "), missingClassesIterator.next());
+
+ // Write remaining lines with line separator before.
+ missingClassesIterator.forEachRemaining(
+ missingClassInfo ->
+ writeMissingClass(
+ builder.append(System.lineSeparator()).append("Missing class "), missingClassInfo));
+
return builder.toString();
}
+ private static void writeMissingClass(
+ StringBuilder builder, Entry<ClassReference, MissingClassAccessContexts> missingClassInfo) {
+ writeMissingClass(builder, missingClassInfo.getKey(), missingClassInfo.getValue());
+ }
+
+ private static void writeMissingClass(
+ StringBuilder builder, ClassReference missingClass, MissingClassAccessContexts contexts) {
+ builder
+ .append(missingClass.getTypeName())
+ .append(contexts.getReferencedFromMessageSuffix(missingClass));
+ }
+
public static class Builder {
private boolean fatal;
- private ImmutableSortedSet.Builder<ClassReference> missingClassesBuilder =
- ImmutableSortedSet.orderedBy(Comparator.comparing(ClassReference::getDescriptor));
+ private ImmutableSortedMap.Builder<ClassReference, MissingClassAccessContexts>
+ missingClassesBuilder =
+ ImmutableSortedMap.orderedBy(Comparator.comparing(ClassReference::getDescriptor));
- public MissingClassesDiagnostic.Builder addMissingClasses(Collection<DexType> missingClasses) {
- for (DexType missingClass : missingClasses) {
- missingClassesBuilder.add(Reference.classFromDescriptor(missingClass.toDescriptorString()));
- }
+ public MissingClassesDiagnostic.Builder addMissingClasses(
+ Map<DexType, Set<DexReference>> missingClasses) {
+ missingClasses.forEach(
+ (missingClass, contexts) ->
+ missingClassesBuilder.put(
+ Reference.classFromDescriptor(missingClass.toDescriptorString()),
+ MissingClassAccessContexts.builder().addAll(contexts).build()));
return this;
}
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationRule.java b/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationRule.java
index aac168d..fbedd8e 100644
--- a/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationRule.java
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationRule.java
@@ -6,6 +6,8 @@
import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexClass;
+import com.android.tools.r8.graph.DexDefinitionSupplier;
+import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.SubtypingInfo;
@@ -22,6 +24,9 @@
public abstract class ProguardConfigurationRule extends ProguardClassSpecification {
private boolean used = false;
+ // TODO(b/164019179): Since we are using the rule language for tracing main dex we can end up in
+ // a situation where the references to types are dead.
+ private boolean canReferenceDeadTypes = false;
ProguardConfigurationRule(
Origin origin,
@@ -101,13 +106,32 @@
return null;
}
+ public void canReferenceDeadTypes() {
+ this.canReferenceDeadTypes = true;
+ }
+
Iterable<DexProgramClass> relevantCandidatesForRule(
AppView<? extends AppInfoWithClassHierarchy> appView,
SubtypingInfo subtypingInfo,
Iterable<DexProgramClass> defaultValue) {
List<DexType> specificTypes = getClassNames().asSpecificDexTypes();
if (specificTypes != null) {
- return DexProgramClass.asProgramClasses(specificTypes, appView);
+ return DexProgramClass.asProgramClasses(
+ specificTypes,
+ new DexDefinitionSupplier() {
+ @Override
+ public DexClass definitionFor(DexType type) {
+ if (canReferenceDeadTypes) {
+ return appView.appInfo().definitionForWithoutExistenceAssert(type);
+ }
+ return appView.definitionFor(type);
+ }
+
+ @Override
+ public DexItemFactory dexItemFactory() {
+ return appView.dexItemFactory();
+ }
+ });
}
if (hasInheritanceClassName() && getInheritanceClassName().hasSpecificType()) {
DexType type = getInheritanceClassName().getSpecificType();
diff --git a/src/main/java/com/android/tools/r8/shaking/RootSetUtils.java b/src/main/java/com/android/tools/r8/shaking/RootSetUtils.java
index f4567e9..8848ece 100644
--- a/src/main/java/com/android/tools/r8/shaking/RootSetUtils.java
+++ b/src/main/java/com/android/tools/r8/shaking/RootSetUtils.java
@@ -4,6 +4,7 @@
package com.android.tools.r8.shaking;
import static com.android.tools.r8.graph.DexProgramClass.asProgramClassOrNull;
+import static com.android.tools.r8.utils.LensUtils.rewriteAndApplyIfNotPrimitiveType;
import com.android.tools.r8.dex.Constants;
import com.android.tools.r8.errors.AssumeNoSideEffectsRuleForObjectMembersDiagnostic;
@@ -30,6 +31,7 @@
import com.android.tools.r8.graph.GraphLens;
import com.android.tools.r8.graph.ProgramMember;
import com.android.tools.r8.graph.ProgramMethod;
+import com.android.tools.r8.graph.PrunedItems;
import com.android.tools.r8.graph.ResolutionResult.SingleResolutionResult;
import com.android.tools.r8.graph.SubtypingInfo;
import com.android.tools.r8.ir.analysis.proto.GeneratedMessageLiteBuilderShrinker;
@@ -1765,6 +1767,37 @@
public int size() {
return classesWithRules.size() + fieldsWithRules.size() + methodsWithRules.size();
}
+
+ public void forEachReference(
+ BiConsumer<? super DexReference, Set<ProguardKeepRuleBase>> consumer) {
+ forEachClass(consumer);
+ forEachMember(consumer);
+ }
+
+ private MutableItemsWithRules prune(Set<DexType> prunedClasses) {
+ MutableItemsWithRules prunedItemsWithRules = new MutableItemsWithRules();
+ forEachReference(
+ (reference, rules) -> {
+ if (!prunedClasses.contains(reference.getContextType())) {
+ prunedItemsWithRules.addReferenceWithRules(reference, rules);
+ }
+ });
+ return prunedItemsWithRules;
+ }
+
+ private MutableItemsWithRules rewrittenWithLens(GraphLens graphLens) {
+ if (graphLens.isIdentityLens()) {
+ return this;
+ }
+ MutableItemsWithRules rewrittenItemsWithRules = new MutableItemsWithRules();
+ forEachReference(
+ (reference, rules) ->
+ rewriteAndApplyIfNotPrimitiveType(
+ graphLens,
+ reference,
+ rewritten -> rewrittenItemsWithRules.addReferenceWithRules(rewritten, rules)));
+ return rewrittenItemsWithRules;
+ }
}
public static class RootSet extends RootSetBase {
@@ -1889,7 +1922,7 @@
}
// Add dependent items that depend on -if rules.
- private static void addDependentItems(
+ static void addDependentItems(
Map<DexReference, ? extends ItemsWithRules> dependentItemsToAdd,
Map<DexReference, MutableItemsWithRules> dependentItemsToAddTo) {
dependentItemsToAdd.forEach(
@@ -2193,4 +2226,165 @@
return new ConsequentRootSetBuilder(appView, subtypingInfo, enqueuer);
}
}
+
+ public static class MainDexRootSetBuilder extends RootSetBuilder {
+
+ private MainDexRootSetBuilder(
+ AppView<? extends AppInfoWithClassHierarchy> appView,
+ SubtypingInfo subtypingInfo,
+ Iterable<? extends ProguardConfigurationRule> rules) {
+ super(appView, subtypingInfo, rules);
+ }
+
+ @Override
+ public MainDexRootSet build(ExecutorService executorService) throws ExecutionException {
+ // Call the super builder to have if-tests calculated automatically.
+ RootSet rootSet = super.build(executorService);
+ return new MainDexRootSet(
+ rootSet.noShrinking,
+ rootSet.reasonAsked,
+ rootSet.checkDiscarded,
+ rootSet.dependentNoShrinking,
+ rootSet.ifRules,
+ rootSet.delayedRootSetActionItems);
+ }
+ }
+
+ public static class MainDexRootSet extends RootSet {
+
+ public MainDexRootSet(
+ MutableItemsWithRules noShrinking,
+ ImmutableList<DexReference> reasonAsked,
+ ImmutableList<DexReference> checkDiscarded,
+ Map<DexReference, MutableItemsWithRules> dependentNoShrinking,
+ Set<ProguardIfRule> ifRules,
+ List<DelayedRootSetActionItem> delayedRootSetActionItems) {
+ super(
+ noShrinking,
+ new MutableItemsWithRules(),
+ Collections.emptySet(),
+ reasonAsked,
+ checkDiscarded,
+ Collections.emptySet(),
+ Collections.emptySet(),
+ Collections.emptySet(),
+ Collections.emptySet(),
+ Collections.emptySet(),
+ Collections.emptySet(),
+ Collections.emptySet(),
+ Collections.emptySet(),
+ Collections.emptySet(),
+ Collections.emptySet(),
+ PredicateSet.empty(),
+ Collections.emptySet(),
+ Collections.emptySet(),
+ Collections.emptySet(),
+ Collections.emptySet(),
+ Collections.emptySet(),
+ Collections.emptyMap(),
+ Collections.emptyMap(),
+ Collections.emptyMap(),
+ dependentNoShrinking,
+ Collections.emptyMap(),
+ Collections.emptyMap(),
+ Collections.emptySet(),
+ ifRules,
+ delayedRootSetActionItems);
+ }
+
+ @Override
+ void addConsequentRootSet(ConsequentRootSet consequentRootSet, boolean addNoShrinking) {
+ if (addNoShrinking) {
+ noShrinking.addAll(consequentRootSet.noShrinking);
+ }
+ addDependentItems(consequentRootSet.dependentNoShrinking, dependentNoShrinking);
+ consequentRootSet.dependentKeepClassCompatRule.forEach(
+ (type, rules) ->
+ dependentKeepClassCompatRule
+ .computeIfAbsent(type, k -> new HashSet<>())
+ .addAll(rules));
+ }
+
+ public static MainDexRootSetBuilder builder(
+ AppView<? extends AppInfoWithClassHierarchy> appView,
+ SubtypingInfo subtypingInfo,
+ Iterable<? extends ProguardConfigurationRule> rules) {
+ return new MainDexRootSetBuilder(appView, subtypingInfo, rules);
+ }
+
+ @Override
+ void shouldNotBeMinified(DexReference reference) {
+ // Do nothing.
+ }
+
+ public MainDexRootSet rewrittenWithLens(GraphLens graphLens) {
+ if (graphLens.isIdentityLens()) {
+ return this;
+ }
+ Map<DexReference, MutableItemsWithRules> rewrittenDependent = new IdentityHashMap<>();
+ dependentNoShrinking.forEach(
+ (reference, rules) -> {
+ // Rewriting a reference can result in us having to merge items with rules.
+ rewriteAndApplyIfNotPrimitiveType(
+ graphLens,
+ reference,
+ rewritten -> {
+ MutableItemsWithRules rewrittenRules =
+ rewrittenDependent.computeIfAbsent(
+ graphLens.lookupReference(reference),
+ rewrittenRef -> new MutableItemsWithRules());
+ rewrittenRules.addAll(rules.rewrittenWithLens(graphLens));
+ });
+ });
+
+ ImmutableList.Builder<DexReference> rewrittenCheckDiscarded = ImmutableList.builder();
+ checkDiscarded.forEach(
+ reference ->
+ rewriteAndApplyIfNotPrimitiveType(
+ graphLens, reference, rewrittenCheckDiscarded::add));
+ ImmutableList.Builder<DexReference> rewrittenReasonAsked = ImmutableList.builder();
+ reasonAsked.forEach(
+ reference ->
+ rewriteAndApplyIfNotPrimitiveType(graphLens, reference, rewrittenReasonAsked::add));
+ // TODO(b/164019179): If rules can now reference dead items. These should be pruned or
+ // rewritten
+ ifRules.forEach(ProguardIfRule::canReferenceDeadTypes);
+ // All delayed root set actions should have been processed at this point.
+ assert delayedRootSetActionItems.isEmpty();
+ return new MainDexRootSet(
+ noShrinking.rewrittenWithLens(graphLens),
+ rewrittenReasonAsked.build(),
+ rewrittenCheckDiscarded.build(),
+ rewrittenDependent,
+ ifRules,
+ delayedRootSetActionItems);
+ }
+
+ public MainDexRootSet withoutPrunedItems(PrunedItems prunedItems) {
+ if (prunedItems.isEmpty()) {
+ return this;
+ }
+ Map<DexReference, MutableItemsWithRules> prunedDependent = new IdentityHashMap<>();
+ dependentNoShrinking.forEach(
+ (ref, rules) -> {
+ if (prunedItems.getRemovedClasses().contains(ref.getContextType())) {
+ // The dependent reference has been pruned and cannot lead to any additional items
+ return;
+ }
+ prunedDependent.put(ref, rules.prune(prunedItems.getRemovedClasses()));
+ });
+ // TODO(b/164019179): If rules can now reference dead items. These should be pruned or
+ // rewritten
+ ifRules.forEach(ProguardIfRule::canReferenceDeadTypes);
+ // All delayed root set actions should have been processed at this point.
+ assert delayedRootSetActionItems.isEmpty();
+ return new MainDexRootSet(
+ noShrinking.prune(prunedItems.getRemovedClasses()),
+ reasonAsked,
+ checkDiscarded,
+ prunedDependent,
+ ifRules,
+ delayedRootSetActionItems);
+ }
+ }
}
diff --git a/src/main/java/com/android/tools/r8/shaking/TreePruner.java b/src/main/java/com/android/tools/r8/shaking/TreePruner.java
index 9155165..9b7a66b 100644
--- a/src/main/java/com/android/tools/r8/shaking/TreePruner.java
+++ b/src/main/java/com/android/tools/r8/shaking/TreePruner.java
@@ -376,7 +376,9 @@
private boolean verifyNoDeadFields(DexProgramClass clazz) {
for (DexEncodedField field : clazz.fields()) {
- assert !field.getOptimizationInfo().isDead()
+ // Pinned field which type is never instantiated are always null, they are marked as dead
+ // so their reads and writes are cleared, but they are still in the program.
+ assert !field.getOptimizationInfo().isDead() || appView.appInfo().isPinned(field)
: "Expected field `" + field.field.toSourceString() + "` to be absent";
}
return true;
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 c52fd0f..4963f21 100644
--- a/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java
+++ b/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java
@@ -219,23 +219,22 @@
// All the bridge methods that have been synthesized during vertical class merging.
private final List<SynthesizedBridgeCode> synthesizedBridges = new ArrayList<>();
- private final MainDexTracingResult mainDexClasses;
+ private final MainDexInfo mainDexInfo;
public VerticalClassMerger(
DexApplication application,
AppView<AppInfoWithLiveness> appView,
ExecutorService executorService,
- Timing timing,
- MainDexTracingResult mainDexClasses) {
+ Timing timing) {
this.application = application;
this.appInfo = appView.appInfo();
this.appView = appView;
+ this.mainDexInfo = appInfo.getMainDexInfo();
this.subtypingInfo = appInfo.computeSubtypingInfo();
this.executorService = executorService;
this.methodPoolCollection = new MethodPoolCollection(appView, subtypingInfo);
this.lensBuilder = new VerticalClassMergerGraphLens.Builder(appView.dexItemFactory());
this.timing = timing;
- this.mainDexClasses = mainDexClasses;
Iterable<DexProgramClass> classes = application.classesWithDeterministicOrder();
initializePinnedTypes(classes); // Must be initialized prior to mergeCandidates.
@@ -826,20 +825,8 @@
return;
}
- // For a main dex class in the dependent set only merge with other classes in either main dex
- // set.
- if ((mainDexClasses.getDependencies().contains(clazz.type)
- || mainDexClasses.getDependencies().contains(targetClass.type))
- && !(mainDexClasses.getClasses().contains(clazz.type)
- && mainDexClasses.getClasses().contains(targetClass.type))) {
- return;
- }
-
- // For a main dex class in the root set only merge with other classes in main dex root set.
- if ((mainDexClasses.getRoots().contains(clazz.type)
- || mainDexClasses.getRoots().contains(targetClass.type))
- && !(mainDexClasses.getRoots().contains(clazz.type)
- && mainDexClasses.getRoots().contains(targetClass.type))) {
+ // Check with main dex classes to see if we are allowed to merge.
+ if (!mainDexInfo.canMerge(clazz, targetClass)) {
return;
}
@@ -1670,9 +1657,7 @@
}
// Constructors can have references beyond the root main dex classes. This can increase the
// size of the main dex dependent classes and we should bail out.
- if (mainDexClasses.getRoots().contains(context.type)
- && MainDexDirectReferenceTracer.hasReferencesOutsideFromCode(
- appView.appInfo(), method, mainDexClasses.getRoots())) {
+ if (mainDexInfo.disallowInliningIntoContext(appView.appInfo(), context, method)) {
return AbortReason.MAIN_DEX_ROOT_OUTSIDE_REFERENCE;
}
return null;
diff --git a/src/main/java/com/android/tools/r8/synthesis/SynthesizingContext.java b/src/main/java/com/android/tools/r8/synthesis/SynthesizingContext.java
index 932359d..1067e93 100644
--- a/src/main/java/com/android/tools/r8/synthesis/SynthesizingContext.java
+++ b/src/main/java/com/android/tools/r8/synthesis/SynthesizingContext.java
@@ -14,8 +14,9 @@
import com.android.tools.r8.graph.GraphLens.NonIdentityGraphLens;
import com.android.tools.r8.graph.ProgramDefinition;
import com.android.tools.r8.origin.Origin;
-import com.android.tools.r8.shaking.MainDexClasses;
+import com.android.tools.r8.shaking.MainDexInfo;
import com.android.tools.r8.synthesis.SyntheticNaming.Phase;
+import com.android.tools.r8.synthesis.SyntheticNaming.SyntheticKind;
import java.util.Comparator;
import java.util.Set;
@@ -63,14 +64,27 @@
}
static SynthesizingContext fromSyntheticContextChange(
- DexType syntheticType, SynthesizingContext oldContext, DexItemFactory factory) {
+ SyntheticKind kind,
+ DexType syntheticType,
+ SynthesizingContext oldContext,
+ DexItemFactory factory) {
String descriptor = syntheticType.toDescriptorString();
- int i = descriptor.indexOf(SyntheticNaming.getPhaseSeparator(Phase.INTERNAL));
- if (i <= 0) {
- assert false : "Unexpected synthetic without internal separator: " + syntheticType;
- return null;
+ DexType newContext;
+ if (kind.isFixedSuffixSynthetic) {
+ int i = descriptor.lastIndexOf(kind.descriptor);
+ if (i < 0 || descriptor.length() != i + kind.descriptor.length() + 1) {
+ assert false : "Unexpected fixed synthetic with invalid suffix: " + syntheticType;
+ return null;
+ }
+ newContext = factory.createType(descriptor.substring(0, i) + ";");
+ } else {
+ int i = descriptor.indexOf(SyntheticNaming.getPhaseSeparator(Phase.INTERNAL));
+ if (i <= 0) {
+ assert false : "Unexpected synthetic without internal separator: " + syntheticType;
+ return null;
+ }
+ newContext = factory.createType(descriptor.substring(0, i) + ";");
}
- DexType newContext = factory.createType(descriptor.substring(0, i) + ";");
return newContext == oldContext.getSynthesizingContextType()
? oldContext
: new SynthesizingContext(newContext, newContext, oldContext.inputContextOrigin);
@@ -141,13 +155,13 @@
void addIfDerivedFromMainDexClass(
DexProgramClass externalSyntheticClass,
- MainDexClasses mainDexClasses,
+ MainDexInfo mainDexInfo,
Set<DexType> allMainDexTypes) {
// The input context type (not the annotated context) determines if the derived class is to be
// in main dex.
// TODO(b/168584485): Once resolved allMainDexTypes == mainDexClasses.
if (allMainDexTypes.contains(inputContextType)) {
- mainDexClasses.add(externalSyntheticClass);
+ mainDexInfo.addSyntheticClass(externalSyntheticClass);
}
}
diff --git a/src/main/java/com/android/tools/r8/synthesis/SyntheticDefinition.java b/src/main/java/com/android/tools/r8/synthesis/SyntheticDefinition.java
index eefe1b9..239e7d0 100644
--- a/src/main/java/com/android/tools/r8/synthesis/SyntheticDefinition.java
+++ b/src/main/java/com/android/tools/r8/synthesis/SyntheticDefinition.java
@@ -64,7 +64,7 @@
final HashCode computeHash(
RepresentativeMap map, boolean intermediate, ClassToFeatureSplitMap classToFeatureSplitMap) {
Hasher hasher = Hashing.murmur3_128().newHasher();
- if (intermediate) {
+ if (intermediate || getKind().isFixedSuffixSynthetic) {
// If in intermediate mode, include the context type as sharing is restricted to within a
// single context.
getContext().getSynthesizingContextType().hashWithTypeEquivalence(hasher, map);
@@ -95,7 +95,7 @@
boolean includeContext,
GraphLens graphLens,
ClassToFeatureSplitMap classToFeatureSplitMap) {
- if (includeContext) {
+ if (includeContext || getKind().isFixedSuffixSynthetic) {
int order = getContext().compareTo(other.getContext());
if (order != 0) {
return order;
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 e2e0b55..741831e 100644
--- a/src/main/java/com/android/tools/r8/synthesis/SyntheticFinalization.java
+++ b/src/main/java/com/android/tools/r8/synthesis/SyntheticFinalization.java
@@ -23,7 +23,7 @@
import com.android.tools.r8.graph.TreeFixerBase;
import com.android.tools.r8.ir.code.NumberGenerator;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
-import com.android.tools.r8.shaking.MainDexClasses;
+import com.android.tools.r8.shaking.MainDexInfo;
import com.android.tools.r8.synthesis.SyntheticNaming.SyntheticKind;
import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.SetUtils;
@@ -86,6 +86,11 @@
this.syntheticMethodsMap = syntheticMethodsMap;
}
+ @Override
+ public boolean isSyntheticFinalizationGraphLens() {
+ return true;
+ }
+
// The mapping is many to one, so the inverse is only defined up to equivalence groups.
// Override the access to renamed signatures to first check for synthetic mappings before
// using the original item mappings of the
@@ -225,7 +230,7 @@
assert !appView.appInfo().hasClassHierarchy();
assert !appView.appInfo().hasLiveness();
Result result = appView.getSyntheticItems().computeFinalSynthetics(appView);
- appView.setAppInfo(new AppInfo(result.commit, appView.appInfo().getMainDexClasses()));
+ appView.setAppInfo(new AppInfo(result.commit, appView.appInfo().getMainDexInfo()));
appView.pruneItems(result.prunedItems);
if (result.lens != null) {
appView.setGraphLens(result.lens);
@@ -252,7 +257,7 @@
Result computeFinalSynthetics(AppView<?> appView) {
assert verifyNoNestedSynthetics();
DexApplication application;
- MainDexClasses mainDexClasses = appView.appInfo().getMainDexClasses();
+ MainDexInfo mainDexInfo = appView.appInfo().getMainDexInfo();
Builder lensBuilder = new Builder();
ImmutableMap.Builder<DexType, SyntheticMethodReference> finalMethodsBuilder =
ImmutableMap.builder();
@@ -266,7 +271,6 @@
appView,
computeEquivalences(appView, synthetics.getNonLegacyMethods().values(), generators),
computeEquivalences(appView, synthetics.getNonLegacyClasses().values(), generators),
- mainDexClasses,
lensBuilder,
(clazz, reference) -> {
finalSyntheticProgramDefinitions.add(clazz);
@@ -282,13 +286,9 @@
finalClassesBuilder.build();
handleSynthesizedClassMapping(
- finalSyntheticProgramDefinitions,
- application,
- options,
- mainDexClasses,
- lensBuilder.typeMap);
+ finalSyntheticProgramDefinitions, application, options, mainDexInfo, lensBuilder.typeMap);
- assert appView.appInfo().getMainDexClasses() == mainDexClasses;
+ assert appView.appInfo().getMainDexInfo() == mainDexInfo;
Set<DexType> prunedSynthetics = Sets.newIdentityHashSet();
synthetics.forEachNonLegacyItem(
@@ -352,14 +352,13 @@
List<DexProgramClass> finalSyntheticClasses,
DexApplication application,
InternalOptions options,
- MainDexClasses mainDexClasses,
+ MainDexInfo mainDexInfo,
Map<DexType, DexType> derivedMainDexTypesToIgnore) {
boolean includeSynthesizedClassMappingInOutput = shouldAnnotateSynthetics(options);
if (includeSynthesizedClassMappingInOutput) {
updateSynthesizedClassMapping(application, finalSyntheticClasses);
}
- updateMainDexListWithSynthesizedClassMap(
- application, mainDexClasses, derivedMainDexTypesToIgnore);
+ updateMainDexListWithSynthesizedClassMap(application, mainDexInfo, derivedMainDexTypesToIgnore);
if (!includeSynthesizedClassMappingInOutput) {
clearSynthesizedClassMapping(application);
}
@@ -405,13 +404,13 @@
private void updateMainDexListWithSynthesizedClassMap(
DexApplication application,
- MainDexClasses mainDexClasses,
+ MainDexInfo mainDexInfo,
Map<DexType, DexType> derivedMainDexTypesToIgnore) {
- if (mainDexClasses.isEmpty()) {
+ if (mainDexInfo.isEmpty()) {
return;
}
List<DexProgramClass> newMainDexClasses = new ArrayList<>();
- mainDexClasses.forEach(
+ mainDexInfo.forEachExcludingDependencies(
dexType -> {
DexProgramClass programClass =
DexProgramClass.asProgramClassOrNull(application.definitionFor(dexType));
@@ -429,7 +428,7 @@
}
}
});
- mainDexClasses.addAll(newMainDexClasses);
+ newMainDexClasses.forEach(mainDexInfo::addSyntheticClass);
}
private void clearSynthesizedClassMapping(DexApplication application) {
@@ -443,7 +442,6 @@
AppView<?> appView,
Map<DexType, EquivalenceGroup<SyntheticMethodDefinition>> syntheticMethodGroups,
Map<DexType, EquivalenceGroup<SyntheticProgramClassDefinition>> syntheticClassGroups,
- MainDexClasses mainDexClasses,
Builder lensBuilder,
BiConsumer<DexProgramClass, SyntheticProgramClassReference> addFinalSyntheticClass,
BiConsumer<DexProgramClass, SyntheticMethodReference> addFinalSyntheticMethod) {
@@ -453,7 +451,8 @@
// TODO(b/168584485): Remove this once class-mapping support is removed.
Set<DexType> derivedMainDexTypes = Sets.newIdentityHashSet();
- mainDexClasses.forEach(
+ MainDexInfo mainDexInfo = appView.appInfo().getMainDexInfo();
+ mainDexInfo.forEachExcludingDependencies(
mainDexType -> {
derivedMainDexTypes.add(mainDexType);
DexProgramClass mainDexClass =
@@ -566,7 +565,7 @@
addMainDexAndSynthesizedFromForMember(
member,
externalSyntheticClass,
- mainDexClasses,
+ mainDexInfo,
derivedMainDexTypes,
appForLookup::programDefinitionFor);
}
@@ -588,7 +587,7 @@
addMainDexAndSynthesizedFromForMember(
member,
externalSyntheticClass,
- mainDexClasses,
+ mainDexInfo,
derivedMainDexTypes,
appForLookup::programDefinitionFor);
}
@@ -638,12 +637,12 @@
private static void addMainDexAndSynthesizedFromForMember(
SyntheticDefinition<?, ?, ?> member,
DexProgramClass externalSyntheticClass,
- MainDexClasses mainDexClasses,
+ MainDexInfo mainDexInfo,
Set<DexType> derivedMainDexTypes,
Function<DexType, DexProgramClass> definitions) {
member
.getContext()
- .addIfDerivedFromMainDexClass(externalSyntheticClass, mainDexClasses, derivedMainDexTypes);
+ .addIfDerivedFromMainDexClass(externalSyntheticClass, mainDexInfo, derivedMainDexTypes);
// TODO(b/168584485): Remove this once class-mapping support is removed.
DexProgramClass from = definitions.apply(member.getContext().getSynthesizingContextType());
if (from != null) {
@@ -765,16 +764,17 @@
DexType representativeContext,
Map<DexType, NumberGenerator> generators,
AppView<?> appView) {
+ DexItemFactory factory = appView.dexItemFactory();
+ if (kind.isFixedSuffixSynthetic) {
+ return SyntheticNaming.createExternalType(kind, representativeContext, "", factory);
+ }
NumberGenerator generator =
generators.computeIfAbsent(representativeContext, k -> new NumberGenerator());
DexType externalType;
do {
externalType =
SyntheticNaming.createExternalType(
- kind,
- representativeContext,
- Integer.toString(generator.next()),
- appView.dexItemFactory());
+ kind, representativeContext, Integer.toString(generator.next()), factory);
DexClass clazz = appView.appInfo().definitionForWithoutExistenceAssert(externalType);
if (clazz != null && isNotSyntheticType(clazz.type)) {
assert options.testing.allowConflictingSyntheticTypes
diff --git a/src/main/java/com/android/tools/r8/synthesis/SyntheticItems.java b/src/main/java/com/android/tools/r8/synthesis/SyntheticItems.java
index c198197..14f9cf8 100644
--- a/src/main/java/com/android/tools/r8/synthesis/SyntheticItems.java
+++ b/src/main/java/com/android/tools/r8/synthesis/SyntheticItems.java
@@ -134,7 +134,7 @@
CommittedItems commit =
new CommittedItems(
synthetics.nextSyntheticId, appView.appInfo().app(), committed, ImmutableList.of());
- appView.setAppInfo(new AppInfo(commit, appView.appInfo().getMainDexClasses()));
+ appView.setAppInfo(new AppInfo(commit, appView.appInfo().getMainDexInfo()));
}
// Internal synthetic id creation helpers.
@@ -305,6 +305,36 @@
return clazz;
}
+ public DexProgramClass createFixedClass(
+ SyntheticKind kind,
+ DexProgramClass context,
+ DexItemFactory factory,
+ Consumer<SyntheticProgramClassBuilder> fn) {
+ // Obtain the outer synthesizing context in the case the context itself is synthetic.
+ // This is to ensure a flat input-type -> synthetic-item mapping.
+ SynthesizingContext outerContext = getSynthesizingContext(context);
+ DexType type = SyntheticNaming.createFixedType(kind, outerContext, factory);
+ SyntheticProgramClassBuilder classBuilder =
+ new SyntheticProgramClassBuilder(type, outerContext, factory);
+ fn.accept(classBuilder);
+ DexProgramClass clazz = classBuilder.build();
+ addPendingDefinition(new SyntheticProgramClassDefinition(kind, outerContext, clazz));
+ return clazz;
+ }
+
+ public DexClasspathClass createFixedClasspathClass(
+ SyntheticKind kind, DexClasspathClass context, DexItemFactory factory) {
+ // Obtain the outer synthesizing context in the case the context itself is synthetic.
+ // This is to ensure a flat input-type -> synthetic-item mapping.
+ SynthesizingContext outerContext = SynthesizingContext.fromNonSyntheticInputContext(context);
+ DexType type = SyntheticNaming.createFixedType(kind, outerContext, factory);
+ SyntheticClasspathClassBuilder classBuilder =
+ new SyntheticClasspathClassBuilder(type, outerContext, factory);
+ DexClasspathClass clazz = classBuilder.build();
+ addPendingDefinition(new SyntheticClasspathClassDefinition(kind, outerContext, clazz));
+ return clazz;
+ }
+
/** Create a single synthetic method item. */
public ProgramMethod createMethod(
SyntheticKind kind,
diff --git a/src/main/java/com/android/tools/r8/synthesis/SyntheticMethodReference.java b/src/main/java/com/android/tools/r8/synthesis/SyntheticMethodReference.java
index 09a0f1a..0b53962 100644
--- a/src/main/java/com/android/tools/r8/synthesis/SyntheticMethodReference.java
+++ b/src/main/java/com/android/tools/r8/synthesis/SyntheticMethodReference.java
@@ -66,7 +66,7 @@
if (method != rewritten) {
context =
SynthesizingContext.fromSyntheticContextChange(
- rewritten.holder, context, lens.dexItemFactory());
+ getKind(), rewritten.holder, context, lens.dexItemFactory());
if (context == null) {
return null;
}
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 fc5b278..c433ec0 100644
--- a/src/main/java/com/android/tools/r8/synthesis/SyntheticNaming.java
+++ b/src/main/java/com/android/tools/r8/synthesis/SyntheticNaming.java
@@ -24,6 +24,7 @@
// Class synthetics.
COMPANION_CLASS("CompanionClass", false),
LAMBDA("Lambda", false),
+ INIT_TYPE_ARGUMENT("-IA", false, true),
// Method synthetics.
BACKPORT("Backport", true),
STATIC_INTERFACE_CALL("StaticInterfaceCall", true),
@@ -37,10 +38,17 @@
public final String descriptor;
public final boolean isSingleSyntheticMethod;
+ public final boolean isFixedSuffixSynthetic;
SyntheticKind(String descriptor, boolean isSingleSyntheticMethod) {
+ this(descriptor, isSingleSyntheticMethod, false);
+ }
+
+ SyntheticKind(
+ String descriptor, boolean isSingleSyntheticMethod, boolean isFixedSuffixSynthetic) {
this.descriptor = descriptor;
this.isSingleSyntheticMethod = isSingleSyntheticMethod;
+ this.isFixedSuffixSynthetic = isFixedSuffixSynthetic;
}
public static SyntheticKind fromDescriptor(String descriptor) {
@@ -73,8 +81,15 @@
|| typeName.contains(EXTERNAL_SYNTHETIC_CLASS_SEPARATOR);
}
+ public static DexType createFixedType(
+ SyntheticKind kind, SynthesizingContext context, DexItemFactory factory) {
+ assert kind.isFixedSuffixSynthetic;
+ return createType("", kind, context.getSynthesizingContextType(), "", factory);
+ }
+
static DexType createInternalType(
SyntheticKind kind, SynthesizingContext context, String id, DexItemFactory factory) {
+ assert !kind.isFixedSuffixSynthetic;
return createType(
INTERNAL_SYNTHETIC_CLASS_SEPARATOR,
kind,
@@ -85,7 +100,13 @@
static DexType createExternalType(
SyntheticKind kind, DexType context, String id, DexItemFactory factory) {
- return createType(EXTERNAL_SYNTHETIC_CLASS_SEPARATOR, kind, context, id, factory);
+ assert kind.isFixedSuffixSynthetic == id.isEmpty();
+ return createType(
+ kind.isFixedSuffixSynthetic ? "" : EXTERNAL_SYNTHETIC_CLASS_SEPARATOR,
+ kind,
+ context,
+ id,
+ factory);
}
private static DexType createType(
@@ -134,6 +155,10 @@
static boolean isSynthetic(ClassReference clazz, Phase phase, SyntheticKind kind) {
String typeName = clazz.getTypeName();
+ if (kind.isFixedSuffixSynthetic) {
+ assert phase == null;
+ return clazz.getBinaryName().endsWith(kind.descriptor);
+ }
String separator = getPhaseSeparator(phase);
int i = typeName.indexOf(separator);
return i >= 0 && checkMatchFrom(kind, typeName, i, separator, phase == Phase.EXTERNAL);
diff --git a/src/main/java/com/android/tools/r8/synthesis/SyntheticProgramClassDefinition.java b/src/main/java/com/android/tools/r8/synthesis/SyntheticProgramClassDefinition.java
index 8b42eb6..3506dab 100644
--- a/src/main/java/com/android/tools/r8/synthesis/SyntheticProgramClassDefinition.java
+++ b/src/main/java/com/android/tools/r8/synthesis/SyntheticProgramClassDefinition.java
@@ -48,7 +48,9 @@
@Override
public boolean isValid() {
- return clazz.isPublic() && clazz.isFinal() && clazz.accessFlags.isSynthetic();
+ return clazz.isPublic()
+ && clazz.accessFlags.isSynthetic()
+ && (clazz.isFinal() || clazz.isAbstract());
}
@Override
diff --git a/src/main/java/com/android/tools/r8/synthesis/SyntheticProgramClassReference.java b/src/main/java/com/android/tools/r8/synthesis/SyntheticProgramClassReference.java
index 3472268..7e798e0 100644
--- a/src/main/java/com/android/tools/r8/synthesis/SyntheticProgramClassReference.java
+++ b/src/main/java/com/android/tools/r8/synthesis/SyntheticProgramClassReference.java
@@ -53,7 +53,8 @@
// Ensure that if a synthetic moves its context moves consistently.
if (type != rewritten) {
context =
- SynthesizingContext.fromSyntheticContextChange(rewritten, context, lens.dexItemFactory());
+ SynthesizingContext.fromSyntheticContextChange(
+ getKind(), rewritten, context, lens.dexItemFactory());
if (context == null) {
return null;
}
diff --git a/src/main/java/com/android/tools/r8/tracereferences/Tracer.java b/src/main/java/com/android/tools/r8/tracereferences/Tracer.java
index 286c83d..039f28d 100644
--- a/src/main/java/com/android/tools/r8/tracereferences/Tracer.java
+++ b/src/main/java/com/android/tools/r8/tracereferences/Tracer.java
@@ -31,7 +31,7 @@
import com.android.tools.r8.references.FieldReference;
import com.android.tools.r8.references.MethodReference;
import com.android.tools.r8.references.Reference;
-import com.android.tools.r8.shaking.MainDexClasses;
+import com.android.tools.r8.shaking.MainDexInfo;
import com.android.tools.r8.tracereferences.TraceReferencesConsumer.AccessFlags;
import com.android.tools.r8.tracereferences.TraceReferencesConsumer.ClassAccessFlags;
import com.android.tools.r8.tracereferences.TraceReferencesConsumer.FieldAccessFlags;
@@ -236,7 +236,7 @@
AppInfoWithClassHierarchy.createInitialAppInfoWithClassHierarchy(
application,
ClassToFeatureSplitMap.createEmptyClassToFeatureSplitMap(),
- MainDexClasses.createEmptyMainDexClasses());
+ MainDexInfo.none());
}
void run(TraceReferencesConsumer consumer) {
diff --git a/src/main/java/com/android/tools/r8/utils/FieldReferenceUtils.java b/src/main/java/com/android/tools/r8/utils/FieldReferenceUtils.java
new file mode 100644
index 0000000..cb26884
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/utils/FieldReferenceUtils.java
@@ -0,0 +1,18 @@
+// 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.utils;
+
+import com.android.tools.r8.references.FieldReference;
+
+public class FieldReferenceUtils {
+
+ public static String toSourceString(FieldReference fieldReference) {
+ return fieldReference.getFieldType().getTypeName()
+ + " "
+ + fieldReference.getHolderClass().getTypeName()
+ + "."
+ + fieldReference.getFieldName();
+ }
+}
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 0d4a768..118a5b6 100644
--- a/src/main/java/com/android/tools/r8/utils/InternalOptions.java
+++ b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
@@ -40,7 +40,6 @@
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.ProgramMethod;
-import com.android.tools.r8.graph.classmerging.HorizontallyMergedLambdaClasses;
import com.android.tools.r8.graph.classmerging.VerticallyMergedClasses;
import com.android.tools.r8.horizontalclassmerging.HorizontallyMergedClasses;
import com.android.tools.r8.inspector.internal.InspectorImpl;
@@ -50,7 +49,6 @@
import com.android.tools.r8.ir.desugar.nest.Nest;
import com.android.tools.r8.ir.optimize.Inliner;
import com.android.tools.r8.ir.optimize.enums.EnumDataMap;
-import com.android.tools.r8.ir.optimize.lambda.kotlin.KotlinLambdaGroupIdFactory;
import com.android.tools.r8.origin.Origin;
import com.android.tools.r8.position.Position;
import com.android.tools.r8.references.Reference;
@@ -201,7 +199,6 @@
enableClassInlining = false;
enableClassStaticizer = false;
enableDevirtualization = false;
- enableLambdaMerging = false;
horizontalClassMergerOptions.disable();
enableVerticalClassMerging = false;
enableEnumUnboxing = false;
@@ -488,6 +485,11 @@
return !canUseNestBasedAccess();
}
+ public boolean canUseRecords() {
+ // TODO(b/169645628): Replace by true when records are supported.
+ return testing.canUseRecords;
+ }
+
public Set<String> extensiveLoggingFilter = getExtensiveLoggingFilter();
public Set<String> extensiveInterfaceMethodMinifierLoggingFilter =
getExtensiveInterfaceMethodMinifierLoggingFilter();
@@ -502,8 +504,6 @@
// Flag to turn on/offLoad/store optimization in the Cf back-end.
public boolean enableLoadStoreOptimization = true;
- // Flag to turn on/off lambda class merging in R8.
- public boolean enableLambdaMerging = false;
// Flag to turn on/off desugaring in D8/R8.
public DesugarState desugarState = DesugarState.ON;
// Flag to turn on/off reduction of nest to improve class merging optimizations.
@@ -600,6 +600,8 @@
public boolean printCfg = false;
public String printCfgFile;
public boolean ignoreMissingClasses = false;
+ public boolean reportMissingClassesInEnclosingMethodAttribute = false;
+ public boolean reportMissingClassesInInnerClassAttributes = false;
// EXPERIMENTAL flag to get behaviour as close to Proguard as possible.
public boolean forceProguardCompatibility = false;
@@ -1139,20 +1141,17 @@
public boolean enableConstructorMerging = true;
// TODO(b/174809311): Update or remove the option and its tests after new lambdas synthetics.
public boolean enableJavaLambdaMerging = false;
- public boolean enableKotlinLambdaMerging = true;
public int syntheticArgumentCount = 3;
public int maxGroupSize = 30;
+ // TODO(b/179019716): Add support for merging in presence of annotations.
+ public boolean skipNoClassesOrMembersWithAnnotationsPolicyForTesting = false;
+
public void disable() {
enable = false;
}
- @Deprecated
- public void disableKotlinLambdaMerging() {
- enableKotlinLambdaMerging = false;
- }
-
public void enable() {
enable = true;
}
@@ -1165,10 +1164,6 @@
enableJavaLambdaMerging = true;
}
- public void enableKotlinLambdaMergingIf(boolean enableKotlinLambdaMerging) {
- this.enableKotlinLambdaMerging = enableKotlinLambdaMerging;
- }
-
public int getMaxGroupSize() {
return maxGroupSize;
}
@@ -1192,10 +1187,6 @@
public boolean isJavaLambdaMergingEnabled() {
return enableJavaLambdaMerging;
}
-
- public boolean isKotlinLambdaMergingEnabled() {
- return enableKotlinLambdaMerging;
- }
}
public static class ProtoShrinkingOptions {
@@ -1243,9 +1234,6 @@
public BiConsumer<AppInfoWithLiveness, Enqueuer.Mode> enqueuerInspector = null;
- public Function<DexProgramClass, KotlinLambdaGroupIdFactory> kotlinLambdaMergerFactoryForClass =
- KotlinLambdaGroupIdFactory::getFactoryForClass;
-
public BiConsumer<ProgramMethod, MethodProcessingId> methodProcessingIdConsumer = null;
public Function<AppView<AppInfoWithLiveness>, RepackagingConfiguration>
@@ -1257,9 +1245,6 @@
public BiConsumer<DexItemFactory, HorizontallyMergedClasses> horizontallyMergedClassesConsumer =
ConsumerUtils.emptyBiConsumer();
- public BiConsumer<DexItemFactory, HorizontallyMergedLambdaClasses>
- horizontallyMergedLambdaClassesConsumer = ConsumerUtils.emptyBiConsumer();
-
public BiConsumer<DexItemFactory, EnumDataMap> unboxedEnumsConsumer =
ConsumerUtils.emptyBiConsumer();
@@ -1303,6 +1288,7 @@
public boolean enableSwitchToIfRewriting = true;
public boolean enableEnumUnboxingDebugLogs = false;
public boolean forceRedundantConstNumberRemoval = false;
+ public boolean canUseRecords = false;
public boolean invertConditionals = false;
public boolean placeExceptionalBlocksLast = false;
public boolean dontCreateMarkerInD8 = false;
diff --git a/src/main/java/com/android/tools/r8/utils/LensUtils.java b/src/main/java/com/android/tools/r8/utils/LensUtils.java
index a039fb6..2c67ac3 100644
--- a/src/main/java/com/android/tools/r8/utils/LensUtils.java
+++ b/src/main/java/com/android/tools/r8/utils/LensUtils.java
@@ -6,9 +6,11 @@
import com.android.tools.r8.graph.DexDefinitionSupplier;
import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.graph.DexReference;
import com.android.tools.r8.graph.GraphLens;
import com.google.common.collect.Sets;
import java.util.Set;
+import java.util.function.Consumer;
public class LensUtils {
@@ -20,4 +22,15 @@
}
return result;
}
+
+ public static <T extends DexReference> void rewriteAndApplyIfNotPrimitiveType(
+ GraphLens graphLens, T reference, Consumer<T> rewrittenConsumer) {
+ T rewrittenReference = graphLens.rewriteReference(reference);
+ // Enum unboxing can change a class type to int which leads to errors going forward since
+ // the root set should not have primitive types.
+ if (rewrittenReference.isDexType() && rewrittenReference.asDexType().isPrimitiveType()) {
+ return;
+ }
+ rewrittenConsumer.accept(rewrittenReference);
+ }
}
diff --git a/src/main/java/com/android/tools/r8/utils/MethodReferenceUtils.java b/src/main/java/com/android/tools/r8/utils/MethodReferenceUtils.java
index 7f48f2a..e76f21a 100644
--- a/src/main/java/com/android/tools/r8/utils/MethodReferenceUtils.java
+++ b/src/main/java/com/android/tools/r8/utils/MethodReferenceUtils.java
@@ -4,21 +4,39 @@
package com.android.tools.r8.utils;
+import com.android.tools.r8.references.ArrayReference;
+import com.android.tools.r8.references.ClassReference;
import com.android.tools.r8.references.MethodReference;
+import com.android.tools.r8.references.Reference;
import com.android.tools.r8.references.TypeReference;
+import com.google.common.collect.ImmutableList;
import java.util.Iterator;
public class MethodReferenceUtils {
+ public static MethodReference mainMethod(ClassReference type) {
+ ArrayReference stringArrayType = Reference.array(Reference.classFromClass(String.class), 1);
+ return Reference.method(type, "main", ImmutableList.of(stringArrayType), null);
+ }
+
public static String toSourceStringWithoutHolderAndReturnType(MethodReference methodReference) {
return toSourceString(methodReference, false, false);
}
+ public static String toSourceString(MethodReference methodReference) {
+ return toSourceString(methodReference, true, true);
+ }
+
public static String toSourceString(
MethodReference methodReference, boolean includeHolder, boolean includeReturnType) {
StringBuilder builder = new StringBuilder();
if (includeReturnType) {
- builder.append(methodReference.getReturnType().getTypeName()).append(" ");
+ builder
+ .append(
+ methodReference.getReturnType() != null
+ ? methodReference.getReturnType().getTypeName()
+ : "void")
+ .append(" ");
}
if (includeHolder) {
builder.append(methodReference.getHolderClass().getTypeName()).append(".");
diff --git a/src/main/java/com/android/tools/r8/utils/PredicateSet.java b/src/main/java/com/android/tools/r8/utils/PredicateSet.java
index da15974..0eb186d 100644
--- a/src/main/java/com/android/tools/r8/utils/PredicateSet.java
+++ b/src/main/java/com/android/tools/r8/utils/PredicateSet.java
@@ -24,6 +24,10 @@
predicates.add(predicate);
}
+ public static <T> PredicateSet<T> empty() {
+ return new PredicateSet<>();
+ }
+
public PredicateSet<T> rewriteItems(Function<T, T> mapping) {
PredicateSet<T> set = new PredicateSet<>();
for (T item : elements) {
diff --git a/src/main/java/com/android/tools/r8/utils/SetUtils.java b/src/main/java/com/android/tools/r8/utils/SetUtils.java
index c67225c..a278196 100644
--- a/src/main/java/com/android/tools/r8/utils/SetUtils.java
+++ b/src/main/java/com/android/tools/r8/utils/SetUtils.java
@@ -37,6 +37,14 @@
return result;
}
+ public static <T> Set<T> newIdentityHashSet(Iterable<T> c1, Iterable<T> c2, Iterable<T> c3) {
+ Set<T> result = Sets.newIdentityHashSet();
+ c1.forEach(result::add);
+ c2.forEach(result::add);
+ c3.forEach(result::add);
+ return result;
+ }
+
public static <T> Set<T> newIdentityHashSet(int capacity) {
return Collections.newSetFromMap(new IdentityHashMap<>(capacity));
}
@@ -54,4 +62,11 @@
}
return out;
}
+
+ public static <T> Set<T> unionIdentityHashSet(Set<T> one, Set<T> other) {
+ Set<T> union = Sets.newIdentityHashSet();
+ union.addAll(one);
+ union.addAll(other);
+ return union;
+ }
}
diff --git a/src/test/examplesJava15/records/Main.java b/src/test/examplesJava15/records/EmptyRecord.java
similarity index 60%
copy from src/test/examplesJava15/records/Main.java
copy to src/test/examplesJava15/records/EmptyRecord.java
index 7be696d..5d16e44 100644
--- a/src/test/examplesJava15/records/Main.java
+++ b/src/test/examplesJava15/records/EmptyRecord.java
@@ -4,13 +4,11 @@
package records;
-public class Main {
+public class EmptyRecord {
- record Person(String name, int age) {}
+ record Empty() {}
public static void main(String[] args) {
- Person janeDoe = new Person("Jane Doe", 42);
- System.out.println(janeDoe.name);
- System.out.println(janeDoe.age);
+ System.out.println(new Empty());
}
}
diff --git a/src/test/examplesJava15/records/RecordInstanceOf.java b/src/test/examplesJava15/records/RecordInstanceOf.java
new file mode 100644
index 0000000..591192c
--- /dev/null
+++ b/src/test/examplesJava15/records/RecordInstanceOf.java
@@ -0,0 +1,21 @@
+// 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 records;
+
+public class RecordInstanceOf {
+
+ record Empty() {}
+
+ record Person(String name, int age) {}
+
+ public static void main(String[] args) {
+ Empty empty = new Empty();
+ Person janeDoe = new Person("Jane Doe", 42);
+ Object o = new Object();
+ System.out.println(janeDoe instanceof java.lang.Record);
+ System.out.println(empty instanceof java.lang.Record);
+ System.out.println(o instanceof java.lang.Record);
+ }
+}
diff --git a/src/test/examplesJava15/records/RecordInvokeCustom.java b/src/test/examplesJava15/records/RecordInvokeCustom.java
new file mode 100644
index 0000000..c460f56
--- /dev/null
+++ b/src/test/examplesJava15/records/RecordInvokeCustom.java
@@ -0,0 +1,48 @@
+// 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 records;
+
+public class RecordInvokeCustom {
+
+ record Empty() {}
+
+ record Person(String name, int age) {}
+
+ public static void main(String[] args) {
+ emptyTest();
+ equalityTest();
+ toStringTest();
+ }
+
+ private static void emptyTest() {
+ Empty empty1 = new Empty();
+ Empty empty2 = new Empty();
+ System.out.println(empty1.toString());
+ System.out.println(empty1.equals(empty2));
+ System.out.println(empty1.hashCode() == empty2.hashCode());
+ System.out.println(empty1.toString().equals(empty2.toString()));
+ }
+
+ private static void toStringTest() {
+ Person janeDoe = new Person("Jane Doe", 42);
+ System.out.println(janeDoe.toString());
+ }
+
+ private static void equalityTest() {
+ Person jane1 = new Person("Jane Doe", 42);
+ Person jane2 = new Person("Jane Doe", 42);
+ String nonIdenticalString = "Jane " + (System.currentTimeMillis() > 0 ? "Doe" : "Zan");
+ Person jane3 = new Person(nonIdenticalString, 42);
+ Person bob = new Person("Bob", 42);
+ Person youngJane = new Person("Jane Doe", 22);
+ System.out.println(jane1.equals(jane2));
+ System.out.println(jane1.toString().equals(jane2.toString()));
+ System.out.println(nonIdenticalString == "Jane Doe"); // false.
+ System.out.println(nonIdenticalString.equals("Jane Doe")); // true.
+ System.out.println(jane1.equals(jane3));
+ System.out.println(jane1.equals(bob));
+ System.out.println(jane1.equals(youngJane));
+ }
+}
diff --git a/src/test/examplesJava15/records/RecordReflection.java b/src/test/examplesJava15/records/RecordReflection.java
new file mode 100644
index 0000000..1b94a12
--- /dev/null
+++ b/src/test/examplesJava15/records/RecordReflection.java
@@ -0,0 +1,29 @@
+// 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 records;
+
+import java.util.Arrays;
+
+public class RecordReflection {
+
+ record Empty(){}
+
+ record Person(String name, int age) {}
+
+ record PersonGeneric <S extends CharSequence>(S name, int age) {}
+
+ public static void main(String[] args) {
+ System.out.println(Empty.class.isRecord());
+ System.out.println(Arrays.toString(Empty.class.getRecordComponents()));
+ System.out.println(Person.class.isRecord());
+ System.out.println(Arrays.toString(Person.class.getRecordComponents()));
+ System.out.println(PersonGeneric.class.isRecord());
+ System.out.println(Arrays.toString(PersonGeneric.class.getRecordComponents()));
+ System.out.println(Arrays.toString(PersonGeneric.class.getTypeParameters()));
+ System.out.println(Object.class.isRecord());
+ System.out.println(Arrays.toString(Object.class.getRecordComponents()));
+ }
+
+}
diff --git a/src/test/examplesJava15/records/RecordWithMembers.java b/src/test/examplesJava15/records/RecordWithMembers.java
new file mode 100644
index 0000000..6de2b99
--- /dev/null
+++ b/src/test/examplesJava15/records/RecordWithMembers.java
@@ -0,0 +1,69 @@
+// 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 records;
+
+public class RecordWithMembers {
+
+
+ record PersonWithConstructors(String name, int age) {
+
+ public PersonWithConstructors(String name, int age) {
+ this.name = name + "X";
+ this.age = age;
+ }
+
+ public PersonWithConstructors(String name) {
+ this(name, -1);
+ }
+ }
+
+ record PersonWithMethods(String name, int age) {
+ public static void staticPrint() {
+ System.out.println("print");
+ }
+
+ @Override
+ public String toString() {
+ return name + age;
+ }
+ }
+
+ record PersonWithFields(String name, int age) {
+
+ // Extra instance fields are not allowed on records.
+ public static String globalName;
+
+ }
+
+ public static void main(String[] args) {
+ personWithConstructorTest();
+ personWithMethodsTest();
+ personWithFieldsTest();
+ }
+
+ private static void personWithConstructorTest() {
+ PersonWithConstructors bob = new PersonWithConstructors("Bob", 43);
+ System.out.println(bob.name);
+ System.out.println(bob.age);
+ System.out.println(bob.name());
+ System.out.println(bob.age());
+ PersonWithConstructors felix = new PersonWithConstructors("Felix");
+ System.out.println(felix.name);
+ System.out.println(felix.age);
+ System.out.println(felix.name());
+ System.out.println(felix.age());
+ }
+
+ private static void personWithMethodsTest() {
+ PersonWithMethods.staticPrint();
+ PersonWithMethods bob = new PersonWithMethods("Bob", 43);
+ System.out.println(bob.toString());
+ }
+
+ private static void personWithFieldsTest() {
+ PersonWithFields.globalName = "extra";
+ System.out.println(PersonWithFields.globalName);
+ }
+}
diff --git a/src/test/examplesJava15/records/Main.java b/src/test/examplesJava15/records/SimpleRecord.java
similarity index 80%
rename from src/test/examplesJava15/records/Main.java
rename to src/test/examplesJava15/records/SimpleRecord.java
index 7be696d..1f8fca4 100644
--- a/src/test/examplesJava15/records/Main.java
+++ b/src/test/examplesJava15/records/SimpleRecord.java
@@ -4,7 +4,7 @@
package records;
-public class Main {
+public class SimpleRecord {
record Person(String name, int age) {}
@@ -12,5 +12,7 @@
Person janeDoe = new Person("Jane Doe", 42);
System.out.println(janeDoe.name);
System.out.println(janeDoe.age);
+ System.out.println(janeDoe.name());
+ System.out.println(janeDoe.age());
}
}
diff --git a/src/test/java/com/android/tools/r8/IntermediateCfD8TestBuilder.java b/src/test/java/com/android/tools/r8/IntermediateCfD8TestBuilder.java
index 609fad5..d018732 100644
--- a/src/test/java/com/android/tools/r8/IntermediateCfD8TestBuilder.java
+++ b/src/test/java/com/android/tools/r8/IntermediateCfD8TestBuilder.java
@@ -29,8 +29,7 @@
private IntermediateCfD8TestBuilder(TestState state, AndroidApiLevel apiLevel) {
super(state);
cf2cf = D8TestBuilder.create(state, Backend.CF).setMinApi(apiLevel);
- cf2dex =
- D8TestBuilder.create(state, Backend.DEX).setMinApi(apiLevel).setDisableDesugaring(true);
+ cf2dex = D8TestBuilder.create(state, Backend.DEX).disableDesugaring().setMinApi(apiLevel);
}
public IntermediateCfD8TestBuilder addOptionsModification(Consumer<InternalOptions> fn) {
diff --git a/src/test/java/com/android/tools/r8/KotlinTestBase.java b/src/test/java/com/android/tools/r8/KotlinTestBase.java
index f8dc222..38202a6 100644
--- a/src/test/java/com/android/tools/r8/KotlinTestBase.java
+++ b/src/test/java/com/android/tools/r8/KotlinTestBase.java
@@ -41,10 +41,12 @@
protected final KotlinCompiler kotlinc;
protected final KotlinTargetVersion targetVersion;
+ protected final KotlinTestParameters kotlinParameters;
- protected KotlinTestBase(KotlinTargetVersion targetVersion, KotlinCompiler kotlinc) {
- this.targetVersion = targetVersion;
- this.kotlinc = kotlinc;
+ protected KotlinTestBase(KotlinTestParameters kotlinParameters) {
+ this.targetVersion = kotlinParameters.getTargetVersion();
+ this.kotlinc = kotlinParameters.getCompiler();
+ this.kotlinParameters = kotlinParameters;
}
protected static List<Path> getKotlinFilesInTestPackage(Package pkg) throws IOException {
@@ -125,6 +127,11 @@
return this;
}
+ public Path getForConfiguration(KotlinTestParameters kotlinParameters) {
+ return getForConfiguration(
+ kotlinParameters.getCompiler(), kotlinParameters.getTargetVersion());
+ }
+
public Path getForConfiguration(KotlinCompiler compiler, KotlinTargetVersion targetVersion) {
Map<KotlinTargetVersion, Path> kotlinTargetVersionPathMap = compiledPaths.get(compiler);
if (kotlinTargetVersionPathMap == null) {
diff --git a/src/test/java/com/android/tools/r8/KotlinTestParameters.java b/src/test/java/com/android/tools/r8/KotlinTestParameters.java
new file mode 100644
index 0000000..69a1cff
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/KotlinTestParameters.java
@@ -0,0 +1,95 @@
+// Copyright (c) 2019, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8;
+
+import static org.junit.Assert.assertNotNull;
+
+import com.android.tools.r8.KotlinCompilerTool.KotlinCompiler;
+import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
+import java.util.ArrayList;
+import java.util.List;
+
+public class KotlinTestParameters {
+
+ private final int index;
+ private final KotlinCompiler kotlinc;
+ private final KotlinTargetVersion targetVersion;
+
+ private KotlinTestParameters(
+ KotlinCompiler kotlinc, KotlinTargetVersion targetVersion, int index) {
+ this.index = index;
+ this.kotlinc = kotlinc;
+ this.targetVersion = targetVersion;
+ }
+
+ public KotlinCompiler getCompiler() {
+ return kotlinc;
+ }
+
+ public KotlinTargetVersion getTargetVersion() {
+ return targetVersion;
+ }
+
+ public boolean isFirst() {
+ return index == 0;
+ }
+
+ public static Builder builder() {
+ return new Builder();
+ }
+
+ @Override
+ public String toString() {
+ return kotlinc + "[target=" + targetVersion + "]";
+ }
+
+ public static class Builder {
+
+ private KotlinCompiler[] compilers;
+ private KotlinTargetVersion[] targetVersions;
+
+ private Builder() {}
+
+ public Builder withAllCompilers() {
+ compilers = ToolHelper.getKotlinCompilers();
+ return this;
+ }
+
+ public Builder withAllCompilersAndTargetVersions() {
+ return withAllCompilers().withAllTargetVersions();
+ }
+
+ public Builder withCompiler(KotlinCompiler compiler) {
+ compilers = new KotlinCompiler[] {compiler};
+ return this;
+ }
+
+ public Builder withAllTargetVersions() {
+ targetVersions = KotlinTargetVersion.values();
+ return this;
+ }
+
+ public Builder withTargetVersion(KotlinTargetVersion targetVersion) {
+ targetVersions = new KotlinTargetVersion[] {targetVersion};
+ return this;
+ }
+
+ public KotlinTestParametersCollection build() {
+ validate();
+ List<KotlinTestParameters> testParameters = new ArrayList<>();
+ int index = 0;
+ for (KotlinCompiler kotlinc : compilers) {
+ for (KotlinTargetVersion targetVersion : targetVersions) {
+ testParameters.add(new KotlinTestParameters(kotlinc, targetVersion, index++));
+ }
+ }
+ return new KotlinTestParametersCollection(testParameters);
+ }
+
+ private void validate() {
+ assertNotNull(compilers);
+ assertNotNull(targetVersions);
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/KotlinTestParametersCollection.java b/src/test/java/com/android/tools/r8/KotlinTestParametersCollection.java
new file mode 100644
index 0000000..6851116
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/KotlinTestParametersCollection.java
@@ -0,0 +1,22 @@
+// Copyright (c) 2019, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8;
+
+import java.util.Collection;
+import java.util.Iterator;
+
+public class KotlinTestParametersCollection implements Iterable<KotlinTestParameters> {
+
+ private final Collection<KotlinTestParameters> parameters;
+
+ public KotlinTestParametersCollection(Collection<KotlinTestParameters> parameters) {
+ assert parameters != null;
+ this.parameters = parameters;
+ }
+
+ @Override
+ public Iterator<KotlinTestParameters> iterator() {
+ return parameters.iterator();
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/ProguardTestBuilder.java b/src/test/java/com/android/tools/r8/ProguardTestBuilder.java
index 7aacf12..460246c 100644
--- a/src/test/java/com/android/tools/r8/ProguardTestBuilder.java
+++ b/src/test/java/com/android/tools/r8/ProguardTestBuilder.java
@@ -272,7 +272,7 @@
}
@Override
- public ProguardTestBuilder noDesugaring() {
+ public ProguardTestBuilder disableDesugaring() {
throw new Unimplemented("No support for disabling desugaring");
}
diff --git a/src/test/java/com/android/tools/r8/SwitchDebugLocalsConflictTest.java b/src/test/java/com/android/tools/r8/SwitchDebugLocalsConflictTest.java
index 7a76f4a..d324f38 100644
--- a/src/test/java/com/android/tools/r8/SwitchDebugLocalsConflictTest.java
+++ b/src/test/java/com/android/tools/r8/SwitchDebugLocalsConflictTest.java
@@ -33,7 +33,7 @@
public void test() throws CompilationFailedException {
testForD8()
.addProgramClassFileData(Dump.dump())
- .noDesugaring()
+ .disableDesugaring()
.compileWithExpectedDiagnostics(
diagnotics -> {
diagnotics.assertNoErrors();
diff --git a/src/test/java/com/android/tools/r8/TestBase.java b/src/test/java/com/android/tools/r8/TestBase.java
index b2945dc..a80cc32 100644
--- a/src/test/java/com/android/tools/r8/TestBase.java
+++ b/src/test/java/com/android/tools/r8/TestBase.java
@@ -46,7 +46,7 @@
import com.android.tools.r8.references.TypeReference;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.android.tools.r8.shaking.EnqueuerFactory;
-import com.android.tools.r8.shaking.MainDexClasses;
+import com.android.tools.r8.shaking.MainDexInfo;
import com.android.tools.r8.shaking.NoHorizontalClassMergingRule;
import com.android.tools.r8.shaking.NoVerticalClassMergingRule;
import com.android.tools.r8.shaking.ProguardClassNameList;
@@ -360,8 +360,6 @@
// Actually running Proguard should only be during development.
private static final boolean RUN_PROGUARD = System.getProperty("run_proguard") != null;
- // Actually running r8.jar in a forked process.
- private static final boolean RUN_R8_JAR = System.getProperty("run_r8_jar") != null;
@Rule public ExpectedException thrown = ExpectedException.none();
@@ -395,6 +393,10 @@
return TestParametersBuilder.builder();
}
+ public static KotlinTestParameters.Builder getKotlinTestParameters() {
+ return KotlinTestParameters.builder();
+ }
+
protected static <S, T, E extends Throwable> Function<S, T> memoizeFunction(
ThrowingFunction<S, T, E> fn) {
return CacheBuilder.newBuilder()
@@ -446,13 +448,6 @@
}
/**
- * Check if tests should run R8 in a forked process when applicable.
- */
- protected boolean isRunR8Jar() {
- return RUN_R8_JAR;
- }
-
- /**
* Write lines of text to a temporary file.
*
* The file will include a line separator after the last line.
@@ -715,7 +710,7 @@
return AppInfoWithClassHierarchy.createInitialAppInfoWithClassHierarchy(
readApplicationForDexOutput(app, new InternalOptions()),
ClassToFeatureSplitMap.createEmptyClassToFeatureSplitMap(),
- MainDexClasses.createEmptyMainDexClasses());
+ MainDexInfo.none());
}
protected static AppView<AppInfoWithClassHierarchy> computeAppViewWithClassHierachy(
@@ -1662,6 +1657,10 @@
return dexItemFactory.createType(descriptor(clazz));
}
+ public static DexType toDexType(ClassReference classReference, DexItemFactory dexItemFactory) {
+ return dexItemFactory.createType(classReference.getDescriptor());
+ }
+
public static String binaryName(Class<?> clazz) {
return DescriptorUtils.getBinaryNameFromJavaType(typeName(clazz));
}
diff --git a/src/test/java/com/android/tools/r8/TestCompilerBuilder.java b/src/test/java/com/android/tools/r8/TestCompilerBuilder.java
index b7ca275..ea3f393 100644
--- a/src/test/java/com/android/tools/r8/TestCompilerBuilder.java
+++ b/src/test/java/com/android/tools/r8/TestCompilerBuilder.java
@@ -3,6 +3,7 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8;
+import static com.google.common.base.Predicates.not;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
@@ -20,7 +21,6 @@
import com.android.tools.r8.utils.ThrowingOutputStream;
import com.android.tools.r8.utils.codeinspector.EnumUnboxingInspector;
import com.android.tools.r8.utils.codeinspector.HorizontallyMergedClassesInspector;
-import com.android.tools.r8.utils.codeinspector.HorizontallyMergedLambdaClassesInspector;
import com.android.tools.r8.utils.codeinspector.VerticallyMergedClassesInspector;
import com.google.common.base.Suppliers;
import java.io.ByteArrayOutputStream;
@@ -124,35 +124,24 @@
}
public T addHorizontallyMergedClassesInspector(
- Consumer<HorizontallyMergedClassesInspector> inspector) {
+ ThrowableConsumer<HorizontallyMergedClassesInspector> inspector) {
return addOptionsModification(
options ->
options.testing.horizontallyMergedClassesConsumer =
((dexItemFactory, horizontallyMergedClasses) ->
- inspector.accept(
+ inspector.acceptWithRuntimeException(
new HorizontallyMergedClassesInspector(
dexItemFactory, horizontallyMergedClasses))));
}
public T addHorizontallyMergedClassesInspectorIf(
- boolean condition, Consumer<HorizontallyMergedClassesInspector> inspector) {
+ boolean condition, ThrowableConsumer<HorizontallyMergedClassesInspector> inspector) {
if (condition) {
return addHorizontallyMergedClassesInspector(inspector);
}
return self();
}
- public T addHorizontallyMergedLambdaClassesInspector(
- Consumer<HorizontallyMergedLambdaClassesInspector> inspector) {
- return addOptionsModification(
- options ->
- options.testing.horizontallyMergedLambdaClassesConsumer =
- ((dexItemFactory, horizontallyMergedLambdaClasses) ->
- inspector.accept(
- new HorizontallyMergedLambdaClassesInspector(
- dexItemFactory, horizontallyMergedLambdaClasses))));
- }
-
public T addVerticallyMergedClassesInspector(
Consumer<VerticallyMergedClassesInspector> inspector) {
return addOptionsModification(
@@ -323,11 +312,7 @@
}
public T disableDesugaring() {
- return setDisableDesugaring(true);
- }
-
- public T setDisableDesugaring(boolean disableDesugaring) {
- builder.setDisableDesugaring(disableDesugaring);
+ builder.setDisableDesugaring(true);
return self();
}
@@ -412,11 +397,6 @@
return super.addLibraryProvider(provider);
}
- public T noDesugaring() {
- builder.setDisableDesugaring(true);
- return self();
- }
-
public T allowStdoutMessages() {
allowStdoutMessages = true;
return self();
@@ -540,6 +520,7 @@
public void finished(DiagnosticsHandler handler) {
mainDexClasses =
Stream.of(builder.toString().split(System.lineSeparator()))
+ .filter(not(String::isEmpty))
.map(
line -> {
assert line.endsWith(".class");
diff --git a/src/test/java/com/android/tools/r8/TestDiagnosticMessages.java b/src/test/java/com/android/tools/r8/TestDiagnosticMessages.java
index 4f8713f..997faf6 100644
--- a/src/test/java/com/android/tools/r8/TestDiagnosticMessages.java
+++ b/src/test/java/com/android/tools/r8/TestDiagnosticMessages.java
@@ -19,8 +19,16 @@
List<Diagnostic> getWarnings();
+ default <D extends Diagnostic> D getWarning(int index) {
+ return (D) getWarnings().get(index);
+ }
+
List<Diagnostic> getErrors();
+ default <D extends Diagnostic> D getError(int index) {
+ return (D) getErrors().get(index);
+ }
+
TestDiagnosticMessages assertNoMessages();
TestDiagnosticMessages assertOnlyInfos();
diff --git a/src/test/java/com/android/tools/r8/TestShrinkerBuilder.java b/src/test/java/com/android/tools/r8/TestShrinkerBuilder.java
index 362941b..dbac9f1 100644
--- a/src/test/java/com/android/tools/r8/TestShrinkerBuilder.java
+++ b/src/test/java/com/android/tools/r8/TestShrinkerBuilder.java
@@ -126,39 +126,6 @@
return addDontWarn(clazz.getTypeName() + COMPANION_CLASS_NAME_SUFFIX);
}
- @Deprecated
- public T addDontWarnCompanionClasses() {
- return addDontWarn("**" + COMPANION_CLASS_NAME_SUFFIX);
- }
-
- @Deprecated
- public T addDontWarnCompilerSynthesizedAnnotations() {
- return addDontWarnCompilerSynthesizedClassAnnotation()
- .addDontWarnCompilerSynthesizedClassMapAnnotation();
- }
-
- @Deprecated
- public T addDontWarnCompilerSynthesizedClassAnnotation() {
- return addDontWarn("com.android.tools.r8.annotations.SynthesizedClass");
- }
-
- @Deprecated
- public T addDontWarnCompilerSynthesizedClassMapAnnotation() {
- return addDontWarn("com.android.tools.r8.annotations.SynthesizedClassMap");
- }
-
- // TODO(b/176143558): Should not report missing classes for compiler synthesized classes.
- @Deprecated
- public T addDontWarnEmulatedLibraryClass(Class<?> clazz) {
- return addDontWarn(clazz.getTypeName() + "$-EL");
- }
-
- // TODO(b/176143558): Should not report missing classes for compiler synthesized classes.
- @Deprecated
- public T addDontWarnEmulatedLibraryClasses() {
- return addDontWarn("**$-EL");
- }
-
public T addDontWarnGoogle() {
return addDontWarn("com.google.**");
}
@@ -175,10 +142,6 @@
return addDontWarn("java.lang.invoke.*");
}
- public T addDontWarnJavaLangInvoke(String className) {
- return addDontWarn("java.lang.invoke." + className);
- }
-
public T addDontWarnJavaNioFile() {
return addDontWarn("java.nio.file.**");
}
@@ -213,44 +176,8 @@
return addDontWarn("kotlin.Metadata");
}
- // TODO(b/176133676): Investigate kotlinx missing class references.
- @Deprecated
- public T addDontWarnKotlinx() {
- return addDontWarn("kotlinx.**");
- }
-
- // TODO(b/176144018): Should not report compiler synthesized references as missing.
- @Deprecated
- public T addDontWarnRetargetLibraryMembers() {
- return addDontWarn("j$.retarget.$r8$retargetLibraryMember**");
- }
-
- @Deprecated
- public T addDontWarnRetargetLibraryMember(String suffix) {
- return addDontWarn("j$.retarget.$r8$retargetLibraryMember$" + suffix);
- }
-
- // TODO(b/154849103): Should not warn about SerializedLambda.
- @Deprecated
- public T addDontWarnSerializedLambda() {
- return addDontWarn("java.lang.invoke.SerializedLambda");
- }
-
- // TODO(b/176781593): Should not be reported missing.
- @Deprecated
- public T addDontWarnTimeConversions() {
- return addDontWarn("java.time.TimeConversions");
- }
-
- // TODO(b/176144018): Should not report compiler synthesized references as missing.
- @Deprecated
- public T addDontWarnVivifiedClass(Class<?> clazz) {
- return addDontWarn("$-vivified-$." + clazz.getTypeName());
- }
-
- @Deprecated
- public T addDontWarnVivifiedClasses() {
- return addDontWarn("$-vivified-$.**");
+ public T addIgnoreWarnings() {
+ return addKeepRules("-ignorewarnings");
}
public T addKeepKotlinMetadata() {
@@ -422,9 +349,17 @@
}
public T addKeepAttributes(String... attributes) {
+ return addKeepAttributes(Arrays.asList(attributes));
+ }
+
+ public T addKeepAttributes(List<String> attributes) {
return addKeepRules("-keepattributes " + String.join(",", attributes));
}
+ public T addKeepAttributeExceptions() {
+ return addKeepAttributes(ProguardKeepAttributes.EXCEPTIONS);
+ }
+
public T addKeepAttributeInnerClassesAndEnclosingMethod() {
return addKeepAttributes(
ProguardKeepAttributes.INNER_CLASSES, ProguardKeepAttributes.ENCLOSING_METHOD);
diff --git a/src/test/java/com/android/tools/r8/ThrowableConsumer.java b/src/test/java/com/android/tools/r8/ThrowableConsumer.java
index 664238e..f19039b 100644
--- a/src/test/java/com/android/tools/r8/ThrowableConsumer.java
+++ b/src/test/java/com/android/tools/r8/ThrowableConsumer.java
@@ -20,4 +20,11 @@
default void acceptWithRuntimeException(Formal formal) {
acceptWithHandler(formal, RuntimeException::new);
}
+
+ default ThrowableConsumer<Formal> andThen(ThrowableConsumer<Formal> consumer) {
+ return formal -> {
+ accept(formal);
+ consumer.accept(formal);
+ };
+ }
}
diff --git a/src/test/java/com/android/tools/r8/ToolHelper.java b/src/test/java/com/android/tools/r8/ToolHelper.java
index 08664a8..2ef51ed 100644
--- a/src/test/java/com/android/tools/r8/ToolHelper.java
+++ b/src/test/java/com/android/tools/r8/ToolHelper.java
@@ -1058,14 +1058,8 @@
return String.join("/", parts);
}
- private static List<String> getNamePartsForTestClass(Class clazz) {
- List<String> parts = Lists.newArrayList(clazz.getCanonicalName().split("\\."));
- Class enclosing = clazz;
- while (enclosing.getEnclosingClass() != null) {
- parts.set(parts.size() - 2, parts.get(parts.size() - 2) + "$" + parts.get(parts.size() - 1));
- parts.remove(parts.size() - 1);
- enclosing = enclosing.getEnclosingClass();
- }
+ private static List<String> getNamePartsForTestClass(Class<?> clazz) {
+ List<String> parts = Lists.newArrayList(clazz.getTypeName().split("\\."));
parts.set(parts.size() - 1, parts.get(parts.size() - 1) + ".class");
return parts;
}
@@ -1085,7 +1079,7 @@
.collect(Collectors.toList());
}
- public static Path getClassFileForTestClass(Class clazz) {
+ public static Path getClassFileForTestClass(Class<?> clazz) {
List<String> parts = getNamePartsForTestClass(clazz);
return getClassPathForTests().resolve(Paths.get("", parts.toArray(StringUtils.EMPTY_ARRAY)));
}
diff --git a/src/test/java/com/android/tools/r8/cf/KeepDeserializeLambdaMethodTestRunner.java b/src/test/java/com/android/tools/r8/cf/KeepDeserializeLambdaMethodTestRunner.java
index 902d503..5d84adf 100644
--- a/src/test/java/com/android/tools/r8/cf/KeepDeserializeLambdaMethodTestRunner.java
+++ b/src/test/java/com/android/tools/r8/cf/KeepDeserializeLambdaMethodTestRunner.java
@@ -91,7 +91,6 @@
"-keep class * { private static synthetic void lambda$*(); }");
} else {
builder
- .applyIf(parameters.isDexRuntime(), TestShrinkerBuilder::addDontWarnSerializedLambda)
.noMinification()
.noTreeShaking();
}
diff --git a/src/test/java/com/android/tools/r8/cf/PrintSeedsWithDeserializeLambdaMethodTest.java b/src/test/java/com/android/tools/r8/cf/PrintSeedsWithDeserializeLambdaMethodTest.java
index ee7ff0f..f5673dd 100644
--- a/src/test/java/com/android/tools/r8/cf/PrintSeedsWithDeserializeLambdaMethodTest.java
+++ b/src/test/java/com/android/tools/r8/cf/PrintSeedsWithDeserializeLambdaMethodTest.java
@@ -54,7 +54,6 @@
.addProgramClasses(getClasses())
.setMinApi(parameters.getApiLevel())
.addKeepMainRule(getMainClass())
- .applyIf(parameters.isDexRuntime(), TestShrinkerBuilder::addDontWarnSerializedLambda)
.addPrintSeeds()
.allowStdoutMessages()
.noMinification()
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/ConstructorMergingPreoptimizedTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/ConstructorMergingPreoptimizedTest.java
index f878fd5..4d62a05 100644
--- a/src/test/java/com/android/tools/r8/classmerging/horizontal/ConstructorMergingPreoptimizedTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/ConstructorMergingPreoptimizedTest.java
@@ -36,9 +36,7 @@
options ->
options.horizontalClassMergerOptions().enableIf(enableHorizontalClassMerging))
.addHorizontallyMergedClassesInspectorIf(
- enableHorizontalClassMerging,
- inspector ->
- inspector.assertMerged(A.class, B.class).assertMergedIntoDifferentType(B.class))
+ enableHorizontalClassMerging, inspector -> inspector.assertMergedInto(B.class, A.class))
.enableInliningAnnotations()
.enableNeverClassInliningAnnotations()
.enableNoHorizontalClassMergingAnnotations()
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/NonReboundFieldAccessOnMergedClassTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/NonReboundFieldAccessOnMergedClassTest.java
index ce0425e..029035e 100644
--- a/src/test/java/com/android/tools/r8/classmerging/horizontal/NonReboundFieldAccessOnMergedClassTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/NonReboundFieldAccessOnMergedClassTest.java
@@ -28,12 +28,8 @@
.addOptionsModification(
options ->
options.horizontalClassMergerOptions().enableIf(enableHorizontalClassMerging))
- .addHorizontallyMergedClassesInspector(
- inspector -> {
- if (enableHorizontalClassMerging) {
- inspector.assertMerged(C.class, D.class).assertMergedIntoDifferentType(D.class);
- }
- })
+ .addHorizontallyMergedClassesInspectorIf(
+ enableHorizontalClassMerging, inspector -> inspector.assertMergedInto(D.class, C.class))
.enableNeverClassInliningAnnotations()
.enableNoVerticalClassMergingAnnotations()
.setMinApi(parameters.getApiLevel())
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/NonReboundFieldAccessWithMergedTypeTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/NonReboundFieldAccessWithMergedTypeTest.java
index 79aae15..03b99e0 100644
--- a/src/test/java/com/android/tools/r8/classmerging/horizontal/NonReboundFieldAccessWithMergedTypeTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/NonReboundFieldAccessWithMergedTypeTest.java
@@ -28,13 +28,9 @@
.addOptionsModification(
options ->
options.horizontalClassMergerOptions().enableIf(enableHorizontalClassMerging))
- .addHorizontallyMergedClassesInspector(
- inspector -> {
- if (enableHorizontalClassMerging) {
- inspector.assertMerged(HelloGreeting.class, WorldGreeting.class);
- inspector.assertMergedIntoDifferentType(WorldGreeting.class);
- }
- })
+ .addHorizontallyMergedClassesInspectorIf(
+ enableHorizontalClassMerging,
+ inspector -> inspector.assertMergedInto(WorldGreeting.class, HelloGreeting.class))
.enableNeverClassInliningAnnotations()
.enableNoHorizontalClassMergingAnnotations()
.enableNoVerticalClassMergingAnnotations()
diff --git a/src/test/java/com/android/tools/r8/debug/KotlinStdLibCompilationTest.java b/src/test/java/com/android/tools/r8/debug/KotlinStdLibCompilationTest.java
index ff841d7..86e11d0 100644
--- a/src/test/java/com/android/tools/r8/debug/KotlinStdLibCompilationTest.java
+++ b/src/test/java/com/android/tools/r8/debug/KotlinStdLibCompilationTest.java
@@ -29,7 +29,7 @@
@Parameters(name = "{0}, kotlinc: {1}")
public static List<Object[]> setup() {
return buildParameters(
- TestParametersBuilder.builder().withAllRuntimes().withAllApiLevels().build(),
+ TestParametersBuilder.builder().withAllRuntimesAndApiLevels().build(),
getKotlinCompilers());
}
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/BufferedReaderTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/BufferedReaderTest.java
index 2494871..f71c916 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/BufferedReaderTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/BufferedReaderTest.java
@@ -174,11 +174,6 @@
configurationAlternative3(options, false, parameters))
.addInnerClasses(BufferedReaderTest.class)
.addKeepMainRule(TestClass.class)
- .applyIf(
- parameters.getApiLevel().isLessThan(AndroidApiLevel.N),
- builder ->
- builder.addDontWarnRetargetLibraryMember(
- "virtualDispatch$BufferedReader$lines$dispatchHolder"))
.setMinApi(parameters.getApiLevel())
.enableCoreLibraryDesugaring(parameters.getApiLevel(), keepRuleConsumer)
.enableInliningAnnotations()
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/CustomCollectionForwardingTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/CustomCollectionForwardingTest.java
index 33d3f35..3c822f2 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/CustomCollectionForwardingTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/CustomCollectionForwardingTest.java
@@ -64,9 +64,6 @@
.addKeepMainRule(Executor.class)
.setMinApi(parameters.getApiLevel())
.enableCoreLibraryDesugaring(parameters.getApiLevel(), keepRuleConsumer)
- .applyIf(
- parameters.getApiLevel().isLessThan(AndroidApiLevel.N),
- TestShrinkerBuilder::addDontWarnEmulatedLibraryClasses)
.compile()
.addDesugaredCoreLibraryRunClassPath(
this::buildDesugaredLibrary,
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/CustomCollectionInterfaceSuperTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/CustomCollectionInterfaceSuperTest.java
index 7206b75..684998e 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/CustomCollectionInterfaceSuperTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/CustomCollectionInterfaceSuperTest.java
@@ -81,12 +81,6 @@
testForR8(parameters.getBackend())
.addInnerClasses(CustomCollectionInterfaceSuperTest.class)
.addKeepMainRule(Main.class)
- .applyIf(
- parameters.getApiLevel().isLessThan(AndroidApiLevel.N),
- builder ->
- builder
- .addDontWarnEmulatedLibraryClass(Collection.class)
- .addDontWarnVivifiedClass(Predicate.class))
.setMinApi(parameters.getApiLevel())
.enableCoreLibraryDesugaring(parameters.getApiLevel(), keepRuleConsumer)
.compile()
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/CustomCollectionSuperCallsTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/CustomCollectionSuperCallsTest.java
index ce35ef0..2269987 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/CustomCollectionSuperCallsTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/CustomCollectionSuperCallsTest.java
@@ -108,9 +108,6 @@
testForR8(parameters.getBackend())
.addInnerClasses(CustomCollectionSuperCallsTest.class)
.addKeepMainRule(Executor.class)
- .applyIf(
- parameters.getApiLevel().isLessThan(AndroidApiLevel.N),
- TestShrinkerBuilder::addDontWarnVivifiedClasses)
.setMinApi(parameters.getApiLevel())
.enableCoreLibraryDesugaring(parameters.getApiLevel(), keepRuleConsumer)
.compile()
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/CustomCollectionTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/CustomCollectionTest.java
index 9212761..19cd88f 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/CustomCollectionTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/CustomCollectionTest.java
@@ -133,9 +133,6 @@
.addInnerClasses(CustomCollectionTest.class)
.setMinApi(parameters.getApiLevel())
.addKeepClassAndMembersRules(Executor.class)
- .applyIf(
- requiresEmulatedInterfaceCoreLibDesugaring(parameters),
- builder -> builder.addDontWarnEmulatedLibraryClasses().addDontWarnVivifiedClasses())
.enableCoreLibraryDesugaring(parameters.getApiLevel(), keepRuleConsumer)
.compile()
.inspect(this::assertCustomCollectionCallsCorrect)
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DesugaredLibraryTestBase.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DesugaredLibraryTestBase.java
index 8a9fc12..4de3828 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DesugaredLibraryTestBase.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DesugaredLibraryTestBase.java
@@ -24,7 +24,6 @@
import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.ir.desugar.DesugaredLibraryConfiguration;
import com.android.tools.r8.ir.desugar.DesugaredLibraryConfigurationParser;
-import com.android.tools.r8.ir.desugar.nest.NestBasedAccessDesugaring;
import com.android.tools.r8.origin.Origin;
import com.android.tools.r8.tracereferences.TraceReferences;
import com.android.tools.r8.utils.AndroidApiLevel;
@@ -154,15 +153,8 @@
.startsWith(
"Invalid parameter counts in MethodParameter attributes.")));
}
- // TODO(b/176900254): The nest check should not be necessary.
new CodeInspector(desugaredLib, mapping)
- .forAllClasses(
- clazz ->
- assertTrue(
- clazz.getFinalName().startsWith("j$.")
- || clazz
- .getOriginalName()
- .startsWith(NestBasedAccessDesugaring.NEST_CONSTRUCTOR_NAME)));
+ .forAllClasses(clazz -> assertTrue(clazz.getFinalName().startsWith("j$.")));
return desugaredLib;
} catch (Exception e) {
// Don't wrap assumption violation so junit can catch it.
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DisableDesugarTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DisableDesugarTest.java
index 3cf6d11..a3c1e0f 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DisableDesugarTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DisableDesugarTest.java
@@ -47,7 +47,7 @@
testForD8()
.addInnerClasses(DisableDesugarTest.class)
.setMinApi(parameters.getApiLevel())
- .noDesugaring()
+ .disableDesugaring()
.enableCoreLibraryDesugaring(AndroidApiLevel.B)
.compileWithExpectedDiagnostics(this::checkExpectedDiagnostics);
} catch (CompilationFailedException e) {
@@ -64,7 +64,7 @@
.addInnerClasses(DisableDesugarTest.class)
.addKeepMainRule(TestClass.class)
.setMinApi(parameters.getApiLevel())
- .noDesugaring()
+ .disableDesugaring()
.enableCoreLibraryDesugaring(AndroidApiLevel.B)
.compileWithExpectedDiagnostics(this::checkExpectedDiagnostics);
} catch (CompilationFailedException e) {
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/FeatureSplitTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/FeatureSplitTest.java
index 9447057..bca207f 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/FeatureSplitTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/FeatureSplitTest.java
@@ -248,9 +248,6 @@
SplitterTestBase.simpleSplitProvider(
builder, feature2Path, temp, FeatureClass2.class))
.addKeepAllClassesRule()
- .applyIf(
- parameters.getApiLevel().isLessThan(AndroidApiLevel.N),
- TestShrinkerBuilder::addDontWarnEmulatedLibraryClasses)
.enableCoreLibraryDesugaring(parameters.getApiLevel(), keepRuleConsumer)
.compile()
.writeToZip(basePath);
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/JavaTimeTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/JavaTimeTest.java
index ff7577f..7190c34 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/JavaTimeTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/JavaTimeTest.java
@@ -265,9 +265,6 @@
testForR8(parameters.getBackend())
.addInnerClasses(JavaTimeTest.class)
.addKeepMainRule(TestClass.class)
- .applyIf(
- parameters.getApiLevel().isLessThan(AndroidApiLevel.O),
- TestShrinkerBuilder::addDontWarnRetargetLibraryMembers)
.enableNoVerticalClassMergingAnnotations()
.setMinApi(parameters.getApiLevel())
.enableCoreLibraryDesugaring(parameters.getApiLevel(), keepRuleConsumer)
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/MinimalInterfaceSuperTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/MinimalInterfaceSuperTest.java
index 6c443af..ff1039f 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/MinimalInterfaceSuperTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/MinimalInterfaceSuperTest.java
@@ -46,9 +46,6 @@
testForR8(parameters.getBackend())
.addInnerClasses(MinimalInterfaceSuperTest.class)
.addKeepMainRule(Main.class)
- .applyIf(
- parameters.getApiLevel().isLessThan(AndroidApiLevel.N),
- builder -> builder.addDontWarnVivifiedClass(Predicate.class))
.setMinApi(parameters.getApiLevel())
.enableCoreLibraryDesugaring(parameters.getApiLevel())
.compile()
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/NoForwardingMethodsTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/NoForwardingMethodsTest.java
index da41454..625395f 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/NoForwardingMethodsTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/NoForwardingMethodsTest.java
@@ -66,9 +66,6 @@
.setMinApi(parameters.getApiLevel())
.addKeepClassAndMembersRules(Executor.class)
.enableCoreLibraryDesugaring(parameters.getApiLevel(), keepRuleConsumer)
- .applyIf(
- parameters.getApiLevel().isLessThan(AndroidApiLevel.N),
- builder -> builder.addDontWarnEmulatedLibraryClass(Collection.class))
.compile()
.inspect(this::assertNoForwardingStreamMethod)
.addDesugaredCoreLibraryRunClassPath(
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/ProgramRewritingTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/ProgramRewritingTest.java
index 07f4415..082253b 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/ProgramRewritingTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/ProgramRewritingTest.java
@@ -121,9 +121,6 @@
testForR8(parameters.getBackend())
.minification(minifying)
.addKeepMainRule(TEST_CLASS)
- .applyIf(
- parameters.getApiLevel().isLessThan(AndroidApiLevel.N),
- TestShrinkerBuilder::addDontWarnEmulatedLibraryClasses)
.addProgramFiles(Paths.get(ToolHelper.EXAMPLES_JAVA9_BUILD_DIR + "stream.jar"))
.setMinApi(parameters.getApiLevel())
.addOptionsModification(
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/RetargetOverrideTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/RetargetOverrideTest.java
index 3eb81ed..8df7620 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/RetargetOverrideTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/RetargetOverrideTest.java
@@ -63,13 +63,6 @@
testForR8(Backend.DEX)
.addKeepMainRule(Executor.class)
.addInnerClasses(RetargetOverrideTest.class)
- .applyIf(
- parameters.getApiLevel().isLessThan(AndroidApiLevel.O),
- builder ->
- builder
- .addDontWarnRetargetLibraryMembers()
- .addDontWarnTimeConversions()
- .addDontWarnVivifiedClasses())
.enableCoreLibraryDesugaring(parameters.getApiLevel(), keepRuleConsumer)
.setMinApi(parameters.getApiLevel())
.compile()
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/SynchronizedCollectionTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/SynchronizedCollectionTest.java
index 9496b9c4..27a8590 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/SynchronizedCollectionTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/SynchronizedCollectionTest.java
@@ -81,9 +81,6 @@
testForR8(parameters.getBackend())
.addProgramFiles(INPUT_JAR)
.addKeepMainRule(MAIN_CLASS)
- .applyIf(
- parameters.getApiLevel().isLessThan(AndroidApiLevel.N),
- TestShrinkerBuilder::addDontWarnEmulatedLibraryClasses)
.setMinApi(parameters.getApiLevel())
.enableCoreLibraryDesugaring(parameters.getApiLevel(), keepRuleConsumer)
.compile()
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/CallBackConversionTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/CallBackConversionTest.java
index 42997cb..cb8c24f 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/CallBackConversionTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/CallBackConversionTest.java
@@ -172,9 +172,6 @@
.setMinApi(parameters.getApiLevel())
.addProgramClasses(Impl.class)
.addLibraryClasses(CustomLibClass.class)
- .applyIf(
- parameters.getApiLevel().isLessThan(AndroidApiLevel.N),
- builder -> builder.addDontWarnVivifiedClass(Consumer.class))
.enableCoreLibraryDesugaring(parameters.getApiLevel(), keepRuleConsumer)
.compile()
.inspect(this::assertLibraryOverridesThere)
@@ -192,7 +189,6 @@
.setMinApi(parameters.getApiLevel())
.addProgramClasses(Impl.class)
.addLibraryClasses(CustomLibClass.class)
- .addDontWarnVivifiedClass(Consumer.class)
.enableCoreLibraryDesugaring(parameters.getApiLevel(), keepRuleConsumer)
.compile()
.inspect(this::assertLibraryOverridesThere)
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/ConversionIntroduceInterfaceMethodTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/ConversionIntroduceInterfaceMethodTest.java
index 1298096..ccd3cdf 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/ConversionIntroduceInterfaceMethodTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/ConversionIntroduceInterfaceMethodTest.java
@@ -154,10 +154,6 @@
opt.desugaredLibraryConfiguration =
configurationWithSupportAllCallbacksFromLibrary(
opt, false, parameters, supportAllCallbacksFromLibrary))
- .applyIf(
- parameters.getApiLevel().isLessThan(AndroidApiLevel.N)
- && supportAllCallbacksFromLibrary,
- builder -> builder.addDontWarnVivifiedClass(Consumer.class))
.compile()
.inspect(this::assertDoubleForEach)
.inspect(this::assertWrapperMethodsPresent)
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/DuplicateAPIProgramTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/DuplicateAPIProgramTest.java
index 8fa7780..b62045b 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/DuplicateAPIProgramTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/DuplicateAPIProgramTest.java
@@ -84,9 +84,6 @@
.addKeepMainRule(Executor.class)
.addProgramClasses(Executor.class, MyMap.class)
.addLibraryClasses(CustomLibClass.class)
- .applyIf(
- parameters.getApiLevel().isLessThan(AndroidApiLevel.N),
- builder -> builder.addDontWarnVivifiedClass(BiConsumer.class))
.enableCoreLibraryDesugaring(parameters.getApiLevel(), keepRuleConsumer)
.compile()
.inspect(this::assertDupMethod)
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/gson/GetGenericInterfaceTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/gson/GetGenericInterfaceTest.java
index 0e66723..70dab5d 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/gson/GetGenericInterfaceTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/gson/GetGenericInterfaceTest.java
@@ -74,11 +74,6 @@
testForR8(Backend.DEX)
.addInnerClasses(GetGenericInterfaceTest.class)
.addKeepMainRule(Executor.class)
- .applyIf(
- parameters.getApiLevel().isLessThan(AndroidApiLevel.O),
- builder ->
- builder.addDontWarnRetargetLibraryMember(
- "virtualDispatch$Date$toInstant$dispatchInterface"))
.noMinification()
.setMinApi(parameters.getApiLevel())
.enableCoreLibraryDesugaring(parameters.getApiLevel(), keepRuleConsumer)
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/kotlin/KotlinMetadataTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/kotlin/KotlinMetadataTest.java
index aa5393a..55d4763 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/kotlin/KotlinMetadataTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/kotlin/KotlinMetadataTest.java
@@ -5,7 +5,6 @@
package com.android.tools.r8.desugar.desugaredlibrary.kotlin;
import static com.android.tools.r8.KotlinTestBase.getCompileMemoizer;
-import static com.android.tools.r8.ToolHelper.getKotlinCompilers;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
import static org.hamcrest.CoreMatchers.containsString;
import static org.hamcrest.CoreMatchers.equalTo;
@@ -16,14 +15,13 @@
import com.android.tools.r8.D8TestRunResult;
import com.android.tools.r8.DexIndexedConsumer.ArchiveConsumer;
-import com.android.tools.r8.KotlinCompilerTool.KotlinCompiler;
import com.android.tools.r8.KotlinTestBase.KotlinCompileMemoizer;
+import com.android.tools.r8.KotlinTestParameters;
import com.android.tools.r8.R8FullTestBuilder;
import com.android.tools.r8.R8TestCompileResult;
import com.android.tools.r8.R8TestRunResult;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.ToolHelper;
-import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
import com.android.tools.r8.desugar.desugaredlibrary.DesugaredLibraryTestBase;
import com.android.tools.r8.kotlin.KotlinMetadataWriter;
import com.android.tools.r8.shaking.ProguardKeepAttributes;
@@ -45,30 +43,27 @@
public class KotlinMetadataTest extends DesugaredLibraryTestBase {
private static final String PKG = KotlinMetadataTest.class.getPackage().getName();
- private final TestParameters parameters;
- private final boolean shrinkDesugaredLibrary;
- private final KotlinTargetVersion targetVersion;
- private final KotlinCompiler kotlinCompiler;
private static final String EXPECTED_OUTPUT = "Wuhuu, my special day is: 1997-8-29-2-14";
- @Parameters(name = "{1}, shrinkDesugaredLibrary: {0}, target: {2}, kotlinc: {3}")
+ private final TestParameters parameters;
+ private final KotlinTestParameters kotlinParameters;
+ private final boolean shrinkDesugaredLibrary;
+
+ @Parameters(name = "{0}, {1}, shrinkDesugaredLibrary: {2}")
public static List<Object[]> data() {
return buildParameters(
- BooleanUtils.values(),
getTestParameters().withAllRuntimesAndApiLevels().build(),
- KotlinTargetVersion.values(),
- getKotlinCompilers());
+ getKotlinTestParameters().withAllCompilersAndTargetVersions().build(),
+ BooleanUtils.values());
}
public KotlinMetadataTest(
- boolean shrinkDesugaredLibrary,
TestParameters parameters,
- KotlinTargetVersion targetVersion,
- KotlinCompiler kotlinCompiler) {
- this.shrinkDesugaredLibrary = shrinkDesugaredLibrary;
+ KotlinTestParameters kotlinParameters,
+ boolean shrinkDesugaredLibrary) {
this.parameters = parameters;
- this.targetVersion = targetVersion;
- this.kotlinCompiler = kotlinCompiler;
+ this.kotlinParameters = kotlinParameters;
+ this.shrinkDesugaredLibrary = shrinkDesugaredLibrary;
}
private static KotlinCompileMemoizer compiledJars =
@@ -83,9 +78,9 @@
public void testCf() throws Exception {
assumeTrue(parameters.getRuntime().isCf());
testForRuntime(parameters)
- .addProgramFiles(compiledJars.getForConfiguration(kotlinCompiler, targetVersion))
- .addProgramFiles(ToolHelper.getKotlinStdlibJar(kotlinCompiler))
- .addProgramFiles(ToolHelper.getKotlinReflectJar(kotlinCompiler))
+ .addProgramFiles(compiledJars.getForConfiguration(kotlinParameters))
+ .addProgramFiles(ToolHelper.getKotlinStdlibJar(kotlinParameters.getCompiler()))
+ .addProgramFiles(ToolHelper.getKotlinReflectJar(kotlinParameters.getCompiler()))
.run(parameters.getRuntime(), PKG + ".MainKt")
.assertSuccessWithOutputLines(EXPECTED_OUTPUT);
}
@@ -97,9 +92,9 @@
final File output = temp.newFile("output.zip");
final D8TestRunResult d8TestRunResult =
testForD8()
- .addProgramFiles(compiledJars.getForConfiguration(kotlinCompiler, targetVersion))
- .addProgramFiles(ToolHelper.getKotlinStdlibJar(kotlinCompiler))
- .addProgramFiles(ToolHelper.getKotlinReflectJar(kotlinCompiler))
+ .addProgramFiles(compiledJars.getForConfiguration(kotlinParameters))
+ .addProgramFiles(ToolHelper.getKotlinStdlibJar(kotlinParameters.getCompiler()))
+ .addProgramFiles(ToolHelper.getKotlinReflectJar(kotlinParameters.getCompiler()))
.setProgramConsumer(new ArchiveConsumer(output.toPath(), true))
.setMinApi(parameters.getApiLevel())
.enableCoreLibraryDesugaring(parameters.getApiLevel(), keepRuleConsumer)
@@ -126,9 +121,9 @@
boolean desugarLibrary = parameters.isDexRuntime() && requiresAnyCoreLibDesugaring(parameters);
final R8FullTestBuilder testBuilder =
testForR8(parameters.getBackend())
- .addProgramFiles(compiledJars.getForConfiguration(kotlinCompiler, targetVersion))
- .addProgramFiles(ToolHelper.getKotlinStdlibJar(kotlinCompiler))
- .addProgramFiles(ToolHelper.getKotlinReflectJar(kotlinCompiler))
+ .addProgramFiles(compiledJars.getForConfiguration(kotlinParameters))
+ .addProgramFiles(ToolHelper.getKotlinStdlibJar(kotlinParameters.getCompiler()))
+ .addProgramFiles(ToolHelper.getKotlinReflectJar(kotlinParameters.getCompiler()))
.addKeepMainRule(PKG + ".MainKt")
.addKeepAllClassesRule()
.addKeepAttributes(ProguardKeepAttributes.RUNTIME_VISIBLE_ANNOTATIONS)
diff --git a/src/test/java/com/android/tools/r8/desugar/lambdas/mergedcontext/MergedContextTest.java b/src/test/java/com/android/tools/r8/desugar/lambdas/mergedcontext/MergedContextTest.java
index 35e31d5..86d541e 100644
--- a/src/test/java/com/android/tools/r8/desugar/lambdas/mergedcontext/MergedContextTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/lambdas/mergedcontext/MergedContextTest.java
@@ -42,7 +42,7 @@
.setMinApi(parameters.getApiLevel())
.addHorizontallyMergedClassesInspector(
inspector -> {
- inspector.assertClassNotMerged(C.class);
+ inspector.assertClassesNotMerged(C.class);
inspector.assertMergedInto(B.class, A.class);
})
.run(parameters.getRuntime(), TestClass.class)
diff --git a/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/Java11R8CompilationTest.java b/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/Java11R8CompilationTest.java
index bf9a226..f47e524 100644
--- a/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/Java11R8CompilationTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/Java11R8CompilationTest.java
@@ -53,8 +53,7 @@
// TODO(b/177967938): Investigate why this is needed.
.applyIf(
parameters.getApiLevel().isLessThan(AndroidApiLevel.O),
- builder -> builder.addDontWarnJavaLangInvoke().addDontWarnJavaNioFile(),
- builder -> builder.addDontWarnJavaLangInvoke("StringConcatFactory"))
+ builder -> builder.addDontWarnJavaLangInvoke().addDontWarnJavaNioFile())
.addOptionsModification(opt -> opt.ignoreMissingClasses = true)
.compile()
.inspect(this::assertNotEmpty)
diff --git a/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/MinimumNumberOfBridgesGenerated.java b/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/MinimumNumberOfBridgesGenerated.java
index c55df8c..6557f52 100644
--- a/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/MinimumNumberOfBridgesGenerated.java
+++ b/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/MinimumNumberOfBridgesGenerated.java
@@ -13,7 +13,10 @@
import com.android.tools.r8.TestRuntime.CfVm;
import com.android.tools.r8.ToolHelper.DexVm;
import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.ir.desugar.nest.NestBasedAccessDesugaring;
+import com.android.tools.r8.references.Reference;
+import com.android.tools.r8.synthesis.SyntheticItemsTestUtils;
import com.android.tools.r8.utils.codeinspector.ClassSubject;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
import com.android.tools.r8.utils.codeinspector.FoundMethodSubject;
@@ -79,9 +82,14 @@
private boolean isNestBridge(FoundMethodSubject methodSubject) {
DexEncodedMethod method = methodSubject.getMethod();
if (method.isInstanceInitializer()) {
- return method.method.proto.parameters.size() > 0 && method.method.proto.parameters.values[
- method.method.proto.parameters.size() - 1].toSourceString()
- .contains(NestBasedAccessDesugaring.NEST_CONSTRUCTOR_NAME);
+ if (method.method.proto.parameters.isEmpty()) {
+ return false;
+ }
+ DexType[] formals = method.method.proto.parameters.values;
+ DexType lastFormal = formals[formals.length - 1];
+ return lastFormal.isClassType()
+ && SyntheticItemsTestUtils.isInitializerTypeArgument(
+ Reference.classFromDescriptor(lastFormal.toDescriptorString()));
}
return method.method.name.toString()
.startsWith(NestBasedAccessDesugaring.NEST_ACCESS_NAME_PREFIX);
diff --git a/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/NestCompilationExceptionTest.java b/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/NestCompilationExceptionTest.java
index 66e2ec7..a9e05a3 100644
--- a/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/NestCompilationExceptionTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/NestCompilationExceptionTest.java
@@ -63,9 +63,7 @@
testMissingNestHostError(true);
testIncompleteNestError(true);
}
- // TODO R8:
- // appView.options().reportMissingNestHost(clazz);
- // clazz.clearNestHost();
+
@Test
public void testWarningR8() throws Exception {
testIncompleteNestWarning(false, parameters.isDexRuntime());
@@ -98,7 +96,6 @@
.setMinApi(parameters.getApiLevel())
.addProgramFiles(matchingClasses)
.addLibraryFiles(ToolHelper.getMostRecentAndroidJar())
- .addDontWarn("java.lang.invoke.StringConcatFactory")
.addOptionsModification(
options -> {
options.ignoreMissingClasses = ignoreMissingClasses;
diff --git a/src/test/java/com/android/tools/r8/desugar/records/EmptyRecordTest.java b/src/test/java/com/android/tools/r8/desugar/records/EmptyRecordTest.java
new file mode 100644
index 0000000..92183a7
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/desugar/records/EmptyRecordTest.java
@@ -0,0 +1,45 @@
+// Copyright (c) 2021, 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.records;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestRuntime.CfRuntime;
+import com.android.tools.r8.utils.StringUtils;
+import java.util.List;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class EmptyRecordTest extends TestBase {
+
+ private static final String RECORD_NAME = "EmptyRecord";
+ private static final byte[][] PROGRAM_DATA = RecordTestUtils.getProgramData(RECORD_NAME);
+ private static final String MAIN_TYPE = RecordTestUtils.getMainType(RECORD_NAME);
+ private static final String EXPECTED_RESULT = StringUtils.lines("Empty[]");
+
+ private final TestParameters parameters;
+
+ public EmptyRecordTest(TestParameters parameters) {
+ this.parameters = parameters;
+ }
+
+ @Parameterized.Parameters(name = "{0}")
+ public static List<Object[]> data() {
+ // TODO(b/174431251): This should be replaced with .withCfRuntimes(start = jdk15).
+ return buildParameters(
+ getTestParameters().withCustomRuntime(CfRuntime.getCheckedInJdk15()).build());
+ }
+
+ @Test
+ public void testJvm() throws Exception {
+ testForJvm()
+ .addProgramClassFileData(PROGRAM_DATA)
+ .addVmArguments("--enable-preview")
+ .run(parameters.getRuntime(), MAIN_TYPE)
+ .assertSuccessWithOutput(EXPECTED_RESULT);
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/desugar/records/InvalidRecordAttributeTest.java b/src/test/java/com/android/tools/r8/desugar/records/InvalidRecordAttributeTest.java
new file mode 100644
index 0000000..e936a33
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/desugar/records/InvalidRecordAttributeTest.java
@@ -0,0 +1,111 @@
+// Copyright (c) 2021, 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.records;
+
+import static com.android.tools.r8.DiagnosticsMatcher.diagnosticMessage;
+import static org.hamcrest.CoreMatchers.containsString;
+import static org.junit.Assert.assertThrows;
+
+import com.android.tools.r8.CompilationFailedException;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestDiagnosticMessages;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestRuntime.CfRuntime;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import java.util.List;
+import org.junit.Assume;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+/** Remove this test when Records are supported by default. */
+@RunWith(Parameterized.class)
+public class InvalidRecordAttributeTest extends TestBase {
+
+ private final TestParameters parameters;
+ private final Backend backend;
+
+ private static final String EMPTY_RECORD = "EmptyRecord";
+ private static final byte[][] EMPTY_RECORD_PROGRAM_DATA =
+ RecordTestUtils.getProgramData(EMPTY_RECORD);
+ private static final String SIMPLE_RECORD = "SimpleRecord";
+ private static final byte[][] SIMPLE_RECORD_PROGRAM_DATA =
+ RecordTestUtils.getProgramData(SIMPLE_RECORD);
+
+ @Parameters(name = "{0} back: {1}")
+ public static List<Object[]> data() {
+ // TODO(b/174431251): This should be replaced with .withCfRuntimes(start = jdk15).
+ return buildParameters(
+ getTestParameters().withCustomRuntime(CfRuntime.getCheckedInJdk15()).build(),
+ Backend.values());
+ }
+
+ public InvalidRecordAttributeTest(TestParameters parameters, Backend backend) {
+ this.parameters = parameters;
+ this.backend = backend;
+ }
+
+ @Test
+ public void testD8EmptyRecord() throws Exception {
+ Assume.assumeTrue(backend.isDex());
+ assertThrows(
+ CompilationFailedException.class,
+ () -> {
+ testForD8(backend)
+ .addProgramClassFileData(EMPTY_RECORD_PROGRAM_DATA)
+ .setMinApi(AndroidApiLevel.B)
+ .compileWithExpectedDiagnostics(
+ InvalidRecordAttributeTest::assertUnsupportedRecordError);
+ });
+ }
+
+ @Test
+ public void testD8SimpleRecord() throws Exception {
+ Assume.assumeTrue(backend.isDex());
+ assertThrows(
+ CompilationFailedException.class,
+ () -> {
+ testForD8(backend)
+ .addProgramClassFileData(RecordTestUtils.getProgramData(SIMPLE_RECORD))
+ .setMinApi(AndroidApiLevel.B)
+ .compileWithExpectedDiagnostics(
+ InvalidRecordAttributeTest::assertUnsupportedRecordError);
+ });
+ }
+
+ @Test
+ public void testR8EmptyRecord() throws Exception {
+ assertThrows(
+ CompilationFailedException.class,
+ () -> {
+ testForR8(backend)
+ .addProgramClassFileData(EMPTY_RECORD_PROGRAM_DATA)
+ .setMinApi(AndroidApiLevel.B)
+ .addKeepMainRule(RecordTestUtils.getMainType(EMPTY_RECORD))
+ .compileWithExpectedDiagnostics(
+ InvalidRecordAttributeTest::assertUnsupportedRecordError);
+ });
+ }
+
+ @Test
+ public void testR8SimpleRecord() throws Exception {
+ assertThrows(
+ CompilationFailedException.class,
+ () -> {
+ testForR8(backend)
+ .addProgramClassFileData(SIMPLE_RECORD_PROGRAM_DATA)
+ .setMinApi(AndroidApiLevel.B)
+ .addKeepMainRule(RecordTestUtils.getMainType(SIMPLE_RECORD))
+ .compileWithExpectedDiagnostics(
+ InvalidRecordAttributeTest::assertUnsupportedRecordError);
+ });
+ }
+
+ private static void assertUnsupportedRecordError(TestDiagnosticMessages diagnostics) {
+ diagnostics.assertErrorThatMatches(
+ diagnosticMessage(containsString("Records are not supported")));
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/desugar/records/RecordInstanceOfTest.java b/src/test/java/com/android/tools/r8/desugar/records/RecordInstanceOfTest.java
new file mode 100644
index 0000000..33a3b57
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/desugar/records/RecordInstanceOfTest.java
@@ -0,0 +1,45 @@
+// Copyright (c) 2021, 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.records;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestRuntime.CfRuntime;
+import com.android.tools.r8.utils.StringUtils;
+import java.util.List;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class RecordInstanceOfTest extends TestBase {
+
+ private static final String RECORD_NAME = "RecordInstanceOf";
+ private static final byte[][] PROGRAM_DATA = RecordTestUtils.getProgramData(RECORD_NAME);
+ private static final String MAIN_TYPE = RecordTestUtils.getMainType(RECORD_NAME);
+ private static final String EXPECTED_RESULT = StringUtils.lines("true", "true", "false");
+
+ private final TestParameters parameters;
+
+ public RecordInstanceOfTest(TestParameters parameters) {
+ this.parameters = parameters;
+ }
+
+ @Parameterized.Parameters(name = "{0}")
+ public static List<Object[]> data() {
+ // TODO(b/174431251): This should be replaced with .withCfRuntimes(start = jdk15).
+ return buildParameters(
+ getTestParameters().withCustomRuntime(CfRuntime.getCheckedInJdk15()).build());
+ }
+
+ @Test
+ public void testJvm() throws Exception {
+ testForJvm()
+ .addProgramClassFileData(PROGRAM_DATA)
+ .addVmArguments("--enable-preview")
+ .run(parameters.getRuntime(), MAIN_TYPE)
+ .assertSuccessWithOutput(EXPECTED_RESULT);
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/desugar/records/RecordInvokeCustomTest.java b/src/test/java/com/android/tools/r8/desugar/records/RecordInvokeCustomTest.java
new file mode 100644
index 0000000..1d1e785
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/desugar/records/RecordInvokeCustomTest.java
@@ -0,0 +1,58 @@
+// Copyright (c) 2021, 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.records;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestRuntime.CfRuntime;
+import com.android.tools.r8.utils.StringUtils;
+import java.util.List;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class RecordInvokeCustomTest extends TestBase {
+
+ private static final String RECORD_NAME = "RecordInvokeCustom";
+ private static final byte[][] PROGRAM_DATA = RecordTestUtils.getProgramData(RECORD_NAME);
+ private static final String MAIN_TYPE = RecordTestUtils.getMainType(RECORD_NAME);
+ private static final String EXPECTED_RESULT =
+ StringUtils.lines(
+ "Empty[]",
+ "true",
+ "true",
+ "true",
+ "true",
+ "true",
+ "false",
+ "true",
+ "true",
+ "false",
+ "false",
+ "Person[name=Jane Doe, age=42]");
+
+ private final TestParameters parameters;
+
+ public RecordInvokeCustomTest(TestParameters parameters) {
+ this.parameters = parameters;
+ }
+
+ @Parameterized.Parameters(name = "{0}")
+ public static List<Object[]> data() {
+ // TODO(b/174431251): This should be replaced with .withCfRuntimes(start = jdk15).
+ return buildParameters(
+ getTestParameters().withCustomRuntime(CfRuntime.getCheckedInJdk15()).build());
+ }
+
+ @Test
+ public void testJvm() throws Exception {
+ testForJvm()
+ .addProgramClassFileData(PROGRAM_DATA)
+ .addVmArguments("--enable-preview")
+ .run(parameters.getRuntime(), MAIN_TYPE)
+ .assertSuccessWithOutput(EXPECTED_RESULT);
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/desugar/records/RecordReflectionTest.java b/src/test/java/com/android/tools/r8/desugar/records/RecordReflectionTest.java
new file mode 100644
index 0000000..1bb0a1a
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/desugar/records/RecordReflectionTest.java
@@ -0,0 +1,55 @@
+// Copyright (c) 2021, 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.records;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestRuntime.CfRuntime;
+import com.android.tools.r8.utils.StringUtils;
+import java.util.List;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class RecordReflectionTest extends TestBase {
+
+ private static final String RECORD_NAME = "RecordReflection";
+ private static final byte[][] PROGRAM_DATA = RecordTestUtils.getProgramData(RECORD_NAME);
+ private static final String MAIN_TYPE = RecordTestUtils.getMainType(RECORD_NAME);
+ private static final String EXPECTED_RESULT =
+ StringUtils.lines(
+ "true",
+ "[]",
+ "true",
+ "[java.lang.String name, int age]",
+ "true",
+ "[java.lang.CharSequence name, int age]",
+ "[S]",
+ "false",
+ "null");
+
+ private final TestParameters parameters;
+
+ public RecordReflectionTest(TestParameters parameters) {
+ this.parameters = parameters;
+ }
+
+ @Parameterized.Parameters(name = "{0}")
+ public static List<Object[]> data() {
+ // TODO(b/174431251): This should be replaced with .withCfRuntimes(start = jdk15).
+ return buildParameters(
+ getTestParameters().withCustomRuntime(CfRuntime.getCheckedInJdk15()).build());
+ }
+
+ @Test
+ public void testJvm() throws Exception {
+ testForJvm()
+ .addProgramClassFileData(PROGRAM_DATA)
+ .addVmArguments("--enable-preview")
+ .run(parameters.getRuntime(), MAIN_TYPE)
+ .assertSuccessWithOutput(EXPECTED_RESULT);
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/desugar/records/RecordTestUtils.java b/src/test/java/com/android/tools/r8/desugar/records/RecordTestUtils.java
new file mode 100644
index 0000000..d4a804b
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/desugar/records/RecordTestUtils.java
@@ -0,0 +1,71 @@
+// Copyright (c) 2021, 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.records;
+
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.utils.StringUtils;
+import com.google.common.io.ByteStreams;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.ArrayList;
+import java.util.Enumeration;
+import java.util.List;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipFile;
+
+/**
+ * Records are compiled using: third_party/openjdk/jdk-15/linux/bin/javac --release 15
+ * --enable-preview path/to/file.java
+ */
+public class RecordTestUtils {
+
+ private static final String EXAMPLE_FOLDER = "examplesJava15";
+ private static final String RECORD_FOLDER = "records";
+
+ public static Path jar() {
+ return Paths.get(ToolHelper.TESTS_BUILD_DIR, EXAMPLE_FOLDER, RECORD_FOLDER + ".jar");
+ }
+
+ public static byte[][] getProgramData(String mainClassSimpleName) {
+ byte[][] bytes = classDataFromPrefix(RECORD_FOLDER + "/" + mainClassSimpleName);
+ assert bytes.length > 0 : "Did not find any program data for " + mainClassSimpleName;
+ return bytes;
+ }
+
+ public static String getMainType(String mainClassSimpleName) {
+ return RECORD_FOLDER + "." + mainClassSimpleName;
+ }
+
+ private static byte[][] classDataFromPrefix(String prefix) {
+ Path examplePath = jar();
+ if (!Files.exists(examplePath)) {
+ throw new RuntimeException(
+ "Could not find path "
+ + examplePath
+ + ". Build "
+ + EXAMPLE_FOLDER
+ + " by running tools/gradle.py build"
+ + StringUtils.capitalize(EXAMPLE_FOLDER));
+ }
+ List<byte[]> result = new ArrayList<>();
+ try (ZipFile zipFile = new ZipFile(examplePath.toFile())) {
+ Enumeration<? extends ZipEntry> entries = zipFile.entries();
+ while (entries.hasMoreElements()) {
+ ZipEntry zipEntry = entries.nextElement();
+ if (zipEntry.getName().startsWith(prefix)) {
+ result.add(ByteStreams.toByteArray(zipFile.getInputStream(zipEntry)));
+ }
+ }
+ } catch (IOException e) {
+ throw new RuntimeException("Could not read zip-entry from " + examplePath.toString(), e);
+ }
+ if (result.isEmpty()) {
+ throw new RuntimeException("Did not find any class with prefix " + prefix);
+ }
+ return result.toArray(new byte[0][0]);
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/desugar/records/RecordWithMembersTest.java b/src/test/java/com/android/tools/r8/desugar/records/RecordWithMembersTest.java
new file mode 100644
index 0000000..c0ca829
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/desugar/records/RecordWithMembersTest.java
@@ -0,0 +1,47 @@
+// Copyright (c) 2021, 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.records;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestRuntime.CfRuntime;
+import com.android.tools.r8.utils.StringUtils;
+import java.util.List;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class RecordWithMembersTest extends TestBase {
+
+ private static final String RECORD_NAME = "RecordWithMembers";
+ private static final byte[][] PROGRAM_DATA = RecordTestUtils.getProgramData(RECORD_NAME);
+ private static final String MAIN_TYPE = RecordTestUtils.getMainType(RECORD_NAME);
+ private static final String EXPECTED_RESULT =
+ StringUtils.lines(
+ "BobX", "43", "BobX", "43", "FelixX", "-1", "FelixX", "-1", "print", "Bob43", "extra");
+
+ private final TestParameters parameters;
+
+ public RecordWithMembersTest(TestParameters parameters) {
+ this.parameters = parameters;
+ }
+
+ @Parameterized.Parameters(name = "{0}")
+ public static List<Object[]> data() {
+ // TODO(b/174431251): This should be replaced with .withCfRuntimes(start = jdk15).
+ return buildParameters(
+ getTestParameters().withCustomRuntime(CfRuntime.getCheckedInJdk15()).build());
+ }
+
+ @Test
+ public void testJvm() throws Exception {
+ testForJvm()
+ .addProgramClassFileData(PROGRAM_DATA)
+ .addVmArguments("--enable-preview")
+ .run(parameters.getRuntime(), MAIN_TYPE)
+ .assertSuccessWithOutput(EXPECTED_RESULT);
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/desugar/records/RecordsAttributeTest.java b/src/test/java/com/android/tools/r8/desugar/records/RecordsAttributeTest.java
deleted file mode 100644
index 831c426..0000000
--- a/src/test/java/com/android/tools/r8/desugar/records/RecordsAttributeTest.java
+++ /dev/null
@@ -1,87 +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.desugar.records;
-
-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.assumeFalse;
-import static org.junit.Assume.assumeTrue;
-
-import com.android.tools.r8.CompilationFailedException;
-import com.android.tools.r8.TestBase;
-import com.android.tools.r8.TestParameters;
-import com.android.tools.r8.TestRuntime.CfRuntime;
-import com.android.tools.r8.examples.jdk15.Records;
-import com.android.tools.r8.utils.AndroidApiLevel;
-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 RecordsAttributeTest extends TestBase {
-
- private final Backend backend;
- private final TestParameters parameters;
-
- @Parameters(name = "{0}")
- public static List<Object[]> data() {
- // TODO(b/174431251): This should be replaced with .withCfRuntimes(start = jdk15).
- return buildParameters(
- getTestParameters().withCustomRuntime(CfRuntime.getCheckedInJdk15()).build(),
- Backend.values());
- }
-
- public RecordsAttributeTest(TestParameters parameters, Backend backend) {
- this.parameters = parameters;
- this.backend = backend;
- }
-
- @Test
- public void testJvm() throws Exception {
- assumeFalse(parameters.isNoneRuntime());
- assumeTrue(backend == Backend.CF);
- testForJvm()
- .addRunClasspathFiles(Records.jar())
- .addVmArguments("--enable-preview")
- .run(parameters.getRuntime(), Records.Main.typeName())
- .assertSuccessWithOutputLines("Jane Doe", "42");
- }
-
- @Test
- public void testD8() throws Exception {
- assertThrows(
- CompilationFailedException.class,
- () -> {
- testForD8(backend)
- .addProgramClassFileData(Records.Main.bytes(), Records.Main$Person.bytes())
- .setMinApi(AndroidApiLevel.B)
- .compileWithExpectedDiagnostics(
- diagnostics -> {
- diagnostics.assertErrorThatMatches(
- diagnosticMessage(containsString("Records are not supported")));
- });
- });
- }
-
- @Test
- public void testR8() throws Exception {
- assertThrows(
- CompilationFailedException.class,
- () -> {
- testForR8(backend)
- .addProgramClassFileData(Records.Main.bytes(), Records.Main$Person.bytes())
- .setMinApi(AndroidApiLevel.B)
- .addKeepMainRule(Records.Main.typeName())
- .compileWithExpectedDiagnostics(
- diagnostics -> {
- diagnostics.assertErrorThatMatches(
- diagnosticMessage(containsString("Records are not supported")));
- });
- });
- }
-}
diff --git a/src/test/java/com/android/tools/r8/desugar/records/SimpleRecordTest.java b/src/test/java/com/android/tools/r8/desugar/records/SimpleRecordTest.java
new file mode 100644
index 0000000..35ce924
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/desugar/records/SimpleRecordTest.java
@@ -0,0 +1,46 @@
+// Copyright (c) 2021, 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.records;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestRuntime.CfRuntime;
+import com.android.tools.r8.utils.StringUtils;
+import java.util.List;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class SimpleRecordTest extends TestBase {
+
+ private static final String RECORD_NAME = "SimpleRecord";
+ private static final byte[][] PROGRAM_DATA = RecordTestUtils.getProgramData(RECORD_NAME);
+ private static final String MAIN_TYPE = RecordTestUtils.getMainType(RECORD_NAME);
+ private static final String EXPECTED_RESULT =
+ StringUtils.lines("Jane Doe", "42", "Jane Doe", "42");
+
+ private final TestParameters parameters;
+
+ public SimpleRecordTest(TestParameters parameters) {
+ this.parameters = parameters;
+ }
+
+ @Parameterized.Parameters(name = "{0}")
+ public static List<Object[]> data() {
+ // TODO(b/174431251): This should be replaced with .withCfRuntimes(start = jdk15).
+ return buildParameters(
+ getTestParameters().withCustomRuntime(CfRuntime.getCheckedInJdk15()).build());
+ }
+
+ @Test
+ public void testJvm() throws Exception {
+ testForJvm()
+ .addProgramClassFileData(PROGRAM_DATA)
+ .addVmArguments("--enable-preview")
+ .run(parameters.getRuntime(), MAIN_TYPE)
+ .assertSuccessWithOutput(EXPECTED_RESULT);
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/enumunboxing/kotlin/SimpleKotlinEnumUnboxingTest.java b/src/test/java/com/android/tools/r8/enumunboxing/kotlin/SimpleKotlinEnumUnboxingTest.java
index 3c48865..6f4b565 100644
--- a/src/test/java/com/android/tools/r8/enumunboxing/kotlin/SimpleKotlinEnumUnboxingTest.java
+++ b/src/test/java/com/android/tools/r8/enumunboxing/kotlin/SimpleKotlinEnumUnboxingTest.java
@@ -6,15 +6,13 @@
import static com.android.tools.r8.DiagnosticsMatcher.diagnosticMessage;
import static com.android.tools.r8.KotlinTestBase.getCompileMemoizer;
-import static com.android.tools.r8.ToolHelper.getKotlinCompilers;
import static org.hamcrest.core.StringContains.containsString;
import static org.junit.Assume.assumeTrue;
-import com.android.tools.r8.KotlinCompilerTool.KotlinCompiler;
import com.android.tools.r8.KotlinTestBase.KotlinCompileMemoizer;
+import com.android.tools.r8.KotlinTestParameters;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.ToolHelper;
-import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
import com.android.tools.r8.enumunboxing.EnumUnboxingTestBase;
import com.android.tools.r8.utils.BooleanUtils;
import com.android.tools.r8.utils.DescriptorUtils;
@@ -29,10 +27,9 @@
public class SimpleKotlinEnumUnboxingTest extends EnumUnboxingTestBase {
private final TestParameters parameters;
+ private final KotlinTestParameters kotlinParameters;
private final boolean enumValueOptimization;
private final EnumKeepRules enumKeepRules;
- private final KotlinTargetVersion targetVersion;
- private final KotlinCompiler kotlinCompiler;
private static final String PKG = SimpleKotlinEnumUnboxingTest.class.getPackage().getName();
private static final KotlinCompileMemoizer jars =
@@ -43,27 +40,24 @@
DescriptorUtils.getBinaryNameFromJavaType(PKG),
"Main.kt"));
- @Parameters(name = "{0}, valueOpt: {1}, keep: {2}, kotlin targetVersion: {3}, kotlinc: {4}")
+ @Parameters(name = "{0}, {1}, valueOpt: {2}, keep: {3}")
public static List<Object[]> enumUnboxingTestParameters() {
return buildParameters(
getTestParameters().withAllRuntimesAndApiLevels().build(),
+ getKotlinTestParameters().withAllCompilersAndTargetVersions().build(),
BooleanUtils.values(),
- getAllEnumKeepRules(),
- KotlinTargetVersion.values(),
- getKotlinCompilers());
+ getAllEnumKeepRules());
}
public SimpleKotlinEnumUnboxingTest(
TestParameters parameters,
+ KotlinTestParameters kotlinParameters,
boolean enumValueOptimization,
- EnumKeepRules enumKeepRules,
- KotlinTargetVersion targetVersion,
- KotlinCompiler kotlinCompiler) {
+ EnumKeepRules enumKeepRules) {
this.parameters = parameters;
+ this.kotlinParameters = kotlinParameters;
this.enumValueOptimization = enumValueOptimization;
this.enumKeepRules = enumKeepRules;
- this.targetVersion = targetVersion;
- this.kotlinCompiler = kotlinCompiler;
}
@Test
@@ -71,8 +65,8 @@
assumeTrue(parameters.isDexRuntime());
testForR8(parameters.getBackend())
.addProgramFiles(
- jars.getForConfiguration(kotlinCompiler, targetVersion),
- ToolHelper.getKotlinStdlibJar(kotlinCompiler))
+ jars.getForConfiguration(kotlinParameters),
+ ToolHelper.getKotlinStdlibJar(kotlinParameters.getCompiler()))
.addKeepMainRule(PKG + ".MainKt")
.addKeepRules(enumKeepRules.getKeepRules())
.addKeepRuntimeVisibleAnnotations()
diff --git a/src/test/java/com/android/tools/r8/graph/invokespecial/MultipleInvokeSpecialOnSameClassTest.java b/src/test/java/com/android/tools/r8/graph/invokespecial/MultipleInvokeSpecialOnSameClassTest.java
new file mode 100644
index 0000000..7c934d8
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/graph/invokespecial/MultipleInvokeSpecialOnSameClassTest.java
@@ -0,0 +1,102 @@
+// Copyright (c) 2019, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.graph.invokespecial;
+
+import static org.objectweb.asm.Opcodes.INVOKESPECIAL;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.utils.StringUtils;
+import java.io.IOException;
+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 MultipleInvokeSpecialOnSameClassTest extends TestBase {
+
+ private static final String EXPECTED_RESULT =
+ StringUtils.lines("print", "print2", "print", "print2", "print", "print2");
+
+ private final TestParameters parameters;
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
+ }
+
+ public MultipleInvokeSpecialOnSameClassTest(TestParameters parameters) {
+ this.parameters = parameters;
+ }
+
+ @Test
+ public void testRuntime() throws Exception {
+ testForRuntime(parameters.getRuntime(), parameters.getApiLevel())
+ .addProgramClasses(Main.class)
+ .addProgramClassFileData(getClassWithTransformedInvoked())
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutput(EXPECTED_RESULT);
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ testForR8(parameters.getBackend())
+ .addProgramClasses(Main.class)
+ .addProgramClassFileData(getClassWithTransformedInvoked())
+ .addKeepMainRule(Main.class)
+ .setMinApi(parameters.getApiLevel())
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutput(EXPECTED_RESULT);
+ }
+
+ private byte[] getClassWithTransformedInvoked() throws IOException {
+ return transformer(A.class)
+ .transformMethodInsnInMethod(
+ "bar",
+ (opcode, owner, name, descriptor, isInterface, continuation) -> {
+ continuation.visitMethodInsn(INVOKESPECIAL, owner, name, descriptor, isInterface);
+ })
+ .transformMethodInsnInMethod(
+ "baz",
+ (opcode, owner, name, descriptor, isInterface, continuation) -> {
+ continuation.visitMethodInsn(INVOKESPECIAL, owner, name, descriptor, isInterface);
+ })
+ .transform();
+ }
+
+ public static class A {
+
+ public void print() {
+ System.out.println("print");
+ }
+
+ public void print2() {
+ System.out.println("print2");
+ }
+
+ public void bar() {
+ print(); // Will be rewritten to invoke-special A.print()
+ print2(); // Will be rewritten to invoke-special A.print2()
+ print(); // Will be rewritten to invoke-special A.print()
+ print2(); // Will be rewritten to invoke-special A.print2()
+ }
+
+ public void baz() {
+ print(); // Will be rewritten to invoke-special A.print()
+ print2(); // Will be rewritten to invoke-special A.print2()
+ }
+ }
+
+ public static class Main {
+
+ public static void main(String[] args) {
+ A a = new A();
+ a.bar();
+ a.baz();
+ }
+ }
+}
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 1d7686c..55fcc74 100644
--- a/src/test/java/com/android/tools/r8/ir/IrInjectionTestBase.java
+++ b/src/test/java/com/android/tools/r8/ir/IrInjectionTestBase.java
@@ -14,7 +14,6 @@
import com.android.tools.r8.ir.code.InstructionListIterator;
import com.android.tools.r8.ir.code.NumberGenerator;
import com.android.tools.r8.ir.conversion.IRConverter;
-import com.android.tools.r8.shaking.MainDexTracingResult;
import com.android.tools.r8.smali.SmaliBuilder;
import com.android.tools.r8.smali.SmaliBuilder.MethodSignature;
import com.android.tools.r8.smali.SmaliTestBase;
@@ -113,7 +112,7 @@
public String run() throws IOException {
Timing timing = Timing.empty();
- IRConverter converter = new IRConverter(appView, timing, null, MainDexTracingResult.NONE);
+ IRConverter converter = new IRConverter(appView, timing, null);
converter.replaceCodeForTesting(method, code);
AndroidApp app = writeDex();
return runOnArtRaw(app, DEFAULT_MAIN_CLASS_NAME).stdout;
diff --git a/src/test/java/com/android/tools/r8/ir/analysis/fieldaccess/FieldBitAccessInfoTest.java b/src/test/java/com/android/tools/r8/ir/analysis/fieldaccess/FieldBitAccessInfoTest.java
index 553a2f4..ac86504 100644
--- a/src/test/java/com/android/tools/r8/ir/analysis/fieldaccess/FieldBitAccessInfoTest.java
+++ b/src/test/java/com/android/tools/r8/ir/analysis/fieldaccess/FieldBitAccessInfoTest.java
@@ -25,7 +25,7 @@
import com.android.tools.r8.graph.DirectMappedDexApplication;
import com.android.tools.r8.graph.ProgramMethod;
import com.android.tools.r8.ir.code.IRCode;
-import com.android.tools.r8.ir.conversion.MethodProcessor;
+import com.android.tools.r8.ir.conversion.MethodProcessorWithWave;
import com.android.tools.r8.ir.optimize.info.OptimizationFeedbackIgnore;
import com.android.tools.r8.origin.Origin;
import com.android.tools.r8.utils.AndroidApp;
@@ -209,7 +209,7 @@
}
}
- static class PrimaryMethodProcessorMock extends MethodProcessor {
+ static class PrimaryMethodProcessorMock extends MethodProcessorWithWave {
@Override
public boolean shouldApplyCodeRewritings(ProgramMethod method) {
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/classinliner/ClassInlinerCheckCastWithUnknownReturnTest.java b/src/test/java/com/android/tools/r8/ir/optimize/classinliner/ClassInlinerCheckCastWithUnknownReturnTest.java
new file mode 100644
index 0000000..50a2f54
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/ir/optimize/classinliner/ClassInlinerCheckCastWithUnknownReturnTest.java
@@ -0,0 +1,73 @@
+// Copyright (c) 2021, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.ir.optimize.classinliner;
+
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+import com.android.tools.r8.NeverSingleCallerInline;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+// This is a reproduction of b/176381203.
+@RunWith(Parameterized.class)
+public class ClassInlinerCheckCastWithUnknownReturnTest extends TestBase {
+
+ private final TestParameters parameters;
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
+ }
+
+ public ClassInlinerCheckCastWithUnknownReturnTest(TestParameters parameters) {
+ this.parameters = parameters;
+ }
+
+ @Test()
+ public void testR8() throws Exception {
+ testForR8(parameters.getBackend())
+ .addInnerClasses(getClass())
+ .addKeepMainRule(Main.class)
+ .enableNeverSingleCallerInlineAnnotations()
+ .setMinApi(parameters.getApiLevel())
+ .run(parameters.getRuntime(), Main.class)
+ .assertFailureWithErrorThatThrows(ClassCastException.class)
+ .inspectFailure(
+ inspector -> {
+ ClassSubject aSubject = inspector.clazz(A.class);
+ assertThat(aSubject, isPresent());
+ });
+ }
+
+ public static class A {
+
+ public int number;
+
+ @NeverSingleCallerInline
+ public Object abs() {
+ if (number == 0) {
+ return new Object();
+ }
+ return this;
+ }
+ }
+
+ public static class Main {
+
+ public static void main(String[] args) {
+ A a = new A();
+ a.number = args.length;
+ A returnedA = (A) (a.abs());
+ System.out.println("Hello World");
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/classinliner/ClassInlinerDirectWithUnknownReturnTest.java b/src/test/java/com/android/tools/r8/ir/optimize/classinliner/ClassInlinerDirectWithUnknownReturnTest.java
index 99a169d..0da8e12 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/classinliner/ClassInlinerDirectWithUnknownReturnTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/classinliner/ClassInlinerDirectWithUnknownReturnTest.java
@@ -4,19 +4,20 @@
package com.android.tools.r8.ir.optimize.classinliner;
-import static com.android.tools.r8.DiagnosticsMatcher.diagnosticMessage;
-import static org.hamcrest.CoreMatchers.containsString;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.hamcrest.MatcherAssert.assertThat;
-import com.android.tools.r8.CompilationFailedException;
import com.android.tools.r8.NeverSingleCallerInline;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import org.junit.runners.Parameterized.Parameters;
+// This is a reproduction of b/176381203.
@RunWith(Parameterized.class)
public class ClassInlinerDirectWithUnknownReturnTest extends TestBase {
@@ -31,19 +32,19 @@
this.parameters = parameters;
}
- @Test(expected = CompilationFailedException.class)
+ @Test()
public void testR8() throws Exception {
testForR8(parameters.getBackend())
.addInnerClasses(getClass())
.addKeepMainRule(Main.class)
.enableNeverSingleCallerInlineAnnotations()
.setMinApi(parameters.getApiLevel())
- .compileWithExpectedDiagnostics(
- diagnostics -> {
- diagnostics.assertErrorsMatch(
- diagnosticMessage(
- containsString(
- "Unexpected non-trivial phi in method eligible for class inlining")));
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutputLines("Hello World 0")
+ .inspect(
+ inspector -> {
+ ClassSubject aSubject = inspector.clazz(A.class);
+ assertThat(aSubject, isPresent());
});
}
diff --git a/src/test/java/com/android/tools/r8/kotlin/AbstractR8KotlinTestBase.java b/src/test/java/com/android/tools/r8/kotlin/AbstractR8KotlinTestBase.java
index 0abfe16..0fb02d7 100644
--- a/src/test/java/com/android/tools/r8/kotlin/AbstractR8KotlinTestBase.java
+++ b/src/test/java/com/android/tools/r8/kotlin/AbstractR8KotlinTestBase.java
@@ -14,19 +14,17 @@
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
-import com.android.tools.r8.KotlinCompilerTool.KotlinCompiler;
import com.android.tools.r8.KotlinTestBase;
+import com.android.tools.r8.KotlinTestParameters;
import com.android.tools.r8.R8FullTestBuilder;
import com.android.tools.r8.R8TestRunResult;
import com.android.tools.r8.ThrowableConsumer;
import com.android.tools.r8.ToolHelper;
-import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
import com.android.tools.r8.graph.Code;
import com.android.tools.r8.graph.DexCode;
import com.android.tools.r8.jasmin.JasminBuilder;
import com.android.tools.r8.jasmin.JasminBuilder.ClassBuilder;
import com.android.tools.r8.naming.MemberNaming.MethodSignature;
-import com.android.tools.r8.utils.AndroidApp;
import com.android.tools.r8.utils.DescriptorUtils;
import com.android.tools.r8.utils.StringUtils;
import com.android.tools.r8.utils.codeinspector.ClassSubject;
@@ -52,16 +50,13 @@
private final List<Path> extraClasspath = new ArrayList<>();
// Some tests defined in subclasses, e.g., Metadata tests, don't care about access relaxation.
- protected AbstractR8KotlinTestBase(
- KotlinTargetVersion kotlinTargetVersion, KotlinCompiler kotlinc) {
- this(kotlinTargetVersion, kotlinc, false);
+ protected AbstractR8KotlinTestBase(KotlinTestParameters kotlinParameters) {
+ this(kotlinParameters, false);
}
protected AbstractR8KotlinTestBase(
- KotlinTargetVersion kotlinTargetVersion,
- KotlinCompiler kotlinc,
- boolean allowAccessModification) {
- super(kotlinTargetVersion, kotlinc);
+ KotlinTestParameters kotlinParameters, boolean allowAccessModification) {
+ super(kotlinParameters);
this.allowAccessModification = allowAccessModification;
}
@@ -142,15 +137,6 @@
return fieldSubject;
}
- protected void checkFieldIsRemoved(
- ClassSubject classSubject, String fieldType, String fieldName) {
- // Field must exist in the input.
- checkFieldPresenceInInput(classSubject.getOriginalName(), fieldType, fieldName, true);
- FieldSubject fieldSubject = classSubject.field(fieldType, fieldName);
- assertNotNull(fieldSubject);
- assertThat(fieldSubject, not(isPresent()));
- }
-
protected void checkFieldIsAbsent(ClassSubject classSubject, String fieldType, String fieldName) {
// Field must NOT exist in the input.
checkFieldPresenceInInput(classSubject.getOriginalName(), fieldType, fieldName, false);
@@ -159,12 +145,6 @@
assertFalse(fieldSubject.isPresent());
}
- protected FieldSubject checkFieldIsAbsent(ClassSubject classSubject, String fieldName) {
- FieldSubject fieldSubject = classSubject.uniqueFieldWithName(fieldName);
- assertThat(fieldSubject, not(isPresent()));
- return fieldSubject;
- }
-
protected void checkMethodIsAbsent(ClassSubject classSubject, MethodSignature methodSignature) {
checkMethodPresenceInInput(classSubject.getOriginalName(), methodSignature, false);
checkMethodPresenceInOutput(classSubject, methodSignature, false);
@@ -187,11 +167,6 @@
checkMethodIsKeptOrRemoved(classSubject, methodSignature, false);
}
- protected void checkMethodIsRemoved(ClassSubject classSubject, String methodName) {
- MethodSubject methodSubject = classSubject.uniqueMethodWithName(methodName);
- assertThat(methodSubject, not(isPresent()));
- }
-
protected MethodSubject checkMethodIsKeptOrRemoved(
ClassSubject classSubject, MethodSignature methodSignature, boolean isPresent) {
checkMethodPresenceInInput(classSubject.getOriginalName(), methodSignature, true);
@@ -266,14 +241,13 @@
.addProgramFiles(classpath)
.addKeepMainRule(mainClass)
.allowAccessModification(allowAccessModification)
- .allowDiagnosticMessages()
+ .allowDiagnosticWarningMessages()
.enableProguardTestOptions()
.noMinification()
.apply(configuration)
.compile()
.assertAllWarningMessagesMatch(
containsString("Resource 'META-INF/MANIFEST.MF' already exists."))
- .assertAllInfoMessagesMatch(containsString("Unrecognized Kotlin lambda "))
.run(mainClass)
.assertSuccessWithOutput(javaResult.stdout);
}
@@ -308,12 +282,6 @@
}
}
- @FunctionalInterface
- public interface AndroidAppInspector {
-
- void inspectApp(AndroidApp androidApp) throws Exception;
- }
-
/**
* Generates a "main" class which invokes the given static method (which has no argument and
* return void type). This new class is then added to the test classpath.
diff --git a/src/test/java/com/android/tools/r8/kotlin/KotlinClassInlinerTest.java b/src/test/java/com/android/tools/r8/kotlin/KotlinClassInlinerTest.java
index e4622be..608b92b 100644
--- a/src/test/java/com/android/tools/r8/kotlin/KotlinClassInlinerTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/KotlinClassInlinerTest.java
@@ -4,28 +4,20 @@
package com.android.tools.r8.kotlin;
-import static com.android.tools.r8.ToolHelper.getKotlinCompilers;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
-import static org.hamcrest.CoreMatchers.not;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertTrue;
-import static org.junit.Assume.assumeTrue;
-import com.android.tools.r8.KotlinCompilerTool.KotlinCompiler;
-import com.android.tools.r8.R8FullTestBuilder;
-import com.android.tools.r8.R8TestRunResult;
+import com.android.tools.r8.KotlinTestParameters;
+import com.android.tools.r8.KotlinTestParametersCollection;
import com.android.tools.r8.TestShrinkerBuilder;
-import com.android.tools.r8.ThrowableConsumer;
-import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
import com.android.tools.r8.code.NewInstance;
import com.android.tools.r8.code.SgetObject;
import com.android.tools.r8.graph.DexClass;
import com.android.tools.r8.graph.DexCode;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.naming.MemberNaming.MethodSignature;
-import com.android.tools.r8.utils.BooleanUtils;
import com.android.tools.r8.utils.codeinspector.ClassSubject;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
import com.android.tools.r8.utils.codeinspector.InstructionSubject;
@@ -33,7 +25,6 @@
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import com.google.common.collect.Streams;
-import java.util.Collection;
import java.util.List;
import java.util.Set;
import java.util.function.Predicate;
@@ -46,29 +37,27 @@
@RunWith(Parameterized.class)
public class KotlinClassInlinerTest extends AbstractR8KotlinTestBase {
- @Parameterized.Parameters(name = "target: {0}, kotlinc: {1}, allowAccessModification: {2}")
- public static Collection<Object[]> data() {
- return buildParameters(
- KotlinTargetVersion.values(), getKotlinCompilers(), BooleanUtils.values());
+ @Parameterized.Parameters(name = "{0}")
+ public static KotlinTestParametersCollection data() {
+ return getKotlinTestParameters().withAllCompilersAndTargetVersions().build();
}
- public KotlinClassInlinerTest(
- KotlinTargetVersion targetVersion, KotlinCompiler kotlinc, boolean allowAccessModification) {
- super(targetVersion, kotlinc, allowAccessModification);
+ public KotlinClassInlinerTest(KotlinTestParameters kotlinParameters) {
+ super(kotlinParameters, true);
}
private static boolean isLambda(DexClass clazz) {
- return !clazz.type.getPackageDescriptor().startsWith("kotlin") &&
- (isKStyleLambdaOrGroup(clazz) || isJStyleLambdaOrGroup(clazz));
+ return !clazz.getType().getPackageDescriptor().startsWith("kotlin")
+ && (isKStyleLambda(clazz) || isJStyleLambda(clazz));
}
- private static boolean isKStyleLambdaOrGroup(DexClass clazz) {
- return clazz.superType.descriptor.toString().equals("Lkotlin/jvm/internal/Lambda;");
+ private static boolean isKStyleLambda(DexClass clazz) {
+ return clazz.getSuperType().getTypeName().equals("kotlin.jvm.internal.Lambda");
}
- private static boolean isJStyleLambdaOrGroup(DexClass clazz) {
- return clazz.superType.descriptor.toString().equals("Ljava/lang/Object;") &&
- clazz.interfaces.size() == 1;
+ private static boolean isJStyleLambda(DexClass clazz) {
+ return clazz.getSuperType().getTypeName().equals(Object.class.getTypeName())
+ && clazz.getInterfaces().size() == 1;
}
private static Predicate<DexType> createLambdaCheck(CodeInspector inspector) {
@@ -82,9 +71,8 @@
@Test
public void testJStyleLambdas() throws Exception {
- assumeTrue("Only work with -allowaccessmodification", allowAccessModification);
- final String mainClassName = "class_inliner_lambda_j_style.MainKt";
- runTestWithDefaults(
+ String mainClassName = "class_inliner_lambda_j_style.MainKt";
+ runTest(
"class_inliner_lambda_j_style",
mainClassName,
testBuilder ->
@@ -92,21 +80,32 @@
// TODO(jsjeon): Introduce @NeverInline to kotlinR8TestResources
.addKeepRules("-neverinline class * { void test*State*(...); }")
.addDontWarnJetBrainsNotNullAnnotation()
+ .addHorizontallyMergedClassesInspector(
+ inspector ->
+ inspector
+ .assertIsCompleteMergeGroup(
+ "class_inliner_lambda_j_style.MainKt$testStateless$1",
+ "class_inliner_lambda_j_style.MainKt$testStateless$2",
+ "class_inliner_lambda_j_style.MainKt$testStateless$3")
+ .assertIsCompleteMergeGroup(
+ "class_inliner_lambda_j_style.MainKt$testStateful$1",
+ "class_inliner_lambda_j_style.MainKt$testStateful$2",
+ "class_inliner_lambda_j_style.MainKt$testStateful$2$1",
+ "class_inliner_lambda_j_style.MainKt$testStateful$3",
+ "class_inliner_lambda_j_style.MainKt$testStateful2$1",
+ "class_inliner_lambda_j_style.MainKt$testStateful3$1"))
.noClassInlining())
.inspect(
inspector -> {
assertThat(
+ inspector.clazz("class_inliner_lambda_j_style.MainKt$testStateless$1"),
+ isPresent());
+ assertThat(
inspector.clazz("class_inliner_lambda_j_style.MainKt$testStateful$1"),
isPresent());
- assertThat(
- inspector.clazz("class_inliner_lambda_j_style.MainKt$testStateful2$1"),
- isPresent());
- assertThat(
- inspector.clazz("class_inliner_lambda_j_style.MainKt$testStateful3$1"),
- isPresent());
});
- runTestWithDefaults(
+ runTest(
"class_inliner_lambda_j_style",
mainClassName,
testBuilder ->
@@ -116,40 +115,22 @@
.addDontWarnJetBrainsNotNullAnnotation())
.inspect(
inspector -> {
- Predicate<DexType> lambdaCheck = createLambdaCheck(inspector);
- ClassSubject clazz = inspector.clazz(mainClassName);
+ // TODO(b/173337498): MainKt$testStateless$1 should be class inlined.
+ assertThat(
+ inspector.clazz("class_inliner_lambda_j_style.MainKt$testStateless$1"),
+ isPresent());
- assertEquals(
- Sets.newHashSet(), collectAccessedTypes(lambdaCheck, clazz, "testStateless"));
-
- assertEquals(
- Sets.newHashSet(), collectAccessedTypes(lambdaCheck, clazz, "testStateful"));
-
+ // TODO(b/173337498): MainKt$testStateful$1 should be class inlined.
assertThat(
inspector.clazz("class_inliner_lambda_j_style.MainKt$testStateful$1"),
- not(isPresent()));
-
- assertEquals(
- Sets.newHashSet(), collectAccessedTypes(lambdaCheck, clazz, "testStateful2"));
-
- assertThat(
- inspector.clazz("class_inliner_lambda_j_style.MainKt$testStateful2$1"),
- not(isPresent()));
-
- assertEquals(
- Sets.newHashSet(), collectAccessedTypes(lambdaCheck, clazz, "testStateful3"));
-
- assertThat(
- inspector.clazz("class_inliner_lambda_j_style.MainKt$testStateful3$1"),
- not(isPresent()));
+ isPresent());
});
}
@Test
public void testKStyleLambdas() throws Exception {
- assumeTrue("Only work with -allowaccessmodification", allowAccessModification);
- final String mainClassName = "class_inliner_lambda_k_style.MainKt";
- runTestWithDefaults(
+ String mainClassName = "class_inliner_lambda_k_style.MainKt";
+ runTest(
"class_inliner_lambda_k_style",
mainClassName,
testBuilder ->
@@ -160,6 +141,15 @@
"-neverinline class * { void testBigExtraMethod(...); }",
"-neverinline class * { void testBigExtraMethodReturningLambda(...); }")
.addDontWarnJetBrainsAnnotations()
+ .addHorizontallyMergedClassesInspector(
+ inspector ->
+ inspector.assertIsCompleteMergeGroup(
+ "class_inliner_lambda_k_style.MainKt$testBigExtraMethod$1",
+ "class_inliner_lambda_k_style.MainKt$testBigExtraMethod2$1",
+ "class_inliner_lambda_k_style.MainKt$testBigExtraMethod3$1",
+ "class_inliner_lambda_k_style.MainKt$testBigExtraMethodReturningLambda$1",
+ "class_inliner_lambda_k_style.MainKt$testBigExtraMethodReturningLambda2$1",
+ "class_inliner_lambda_k_style.MainKt$testBigExtraMethodReturningLambda3$1"))
.noClassInlining())
.inspect(
inspector -> {
@@ -174,27 +164,9 @@
assertThat(
inspector.clazz("class_inliner_lambda_k_style.MainKt$testBigExtraMethod$1"),
isPresent());
- assertThat(
- inspector.clazz("class_inliner_lambda_k_style.MainKt$testBigExtraMethod2$1"),
- isPresent());
- assertThat(
- inspector.clazz("class_inliner_lambda_k_style.MainKt$testBigExtraMethod3$1"),
- isPresent());
- assertThat(
- inspector.clazz(
- "class_inliner_lambda_k_style.MainKt$testBigExtraMethodReturningLambda$1"),
- isPresent());
- assertThat(
- inspector.clazz(
- "class_inliner_lambda_k_style.MainKt$testBigExtraMethodReturningLambda2$1"),
- isPresent());
- assertThat(
- inspector.clazz(
- "class_inliner_lambda_k_style.MainKt$testBigExtraMethodReturningLambda3$1"),
- isPresent());
});
- runTestWithDefaults(
+ runTest(
"class_inliner_lambda_k_style",
mainClassName,
testBuilder ->
@@ -207,20 +179,6 @@
.addDontWarnJetBrainsAnnotations())
.inspect(
inspector -> {
- Predicate<DexType> lambdaCheck = createLambdaCheck(inspector);
- ClassSubject clazz = inspector.clazz(mainClassName);
-
- // TODO(b/173337498): Should be empty, but horizontal class merging interferes with
- // class inlining.
- assertEquals(
- Sets.newHashSet(
- "class_inliner_lambda_k_style.MainKt$testKotlinSequencesStateless$1"),
- collectAccessedTypes(
- lambdaCheck,
- clazz,
- "testKotlinSequencesStateless",
- "kotlin.sequences.Sequence"));
-
// TODO(b/173337498): Should be absent, but horizontal class merging interferes with
// class inlining.
assertThat(
@@ -228,19 +186,6 @@
"class_inliner_lambda_k_style.MainKt$testKotlinSequencesStateless$1"),
isPresent());
- // TODO(b/173337498): Should be empty, but horizontal class merging interferes with
- // class inlining.
- assertEquals(
- Sets.newHashSet(
- "class_inliner_lambda_k_style.MainKt$testKotlinSequencesStateful$1"),
- collectAccessedTypes(
- lambdaCheck,
- clazz,
- "testKotlinSequencesStateful",
- "int",
- "int",
- "kotlin.sequences.Sequence"));
-
// TODO(b/173337498): Should be absent, but horizontal class merging interferes with
// class inlining.
assertThat(
@@ -248,57 +193,33 @@
"class_inliner_lambda_k_style.MainKt$testKotlinSequencesStateful$1"),
isPresent());
- assertEquals(
- Sets.newHashSet(),
- collectAccessedTypes(lambdaCheck, clazz, "testBigExtraMethod"));
-
+ // TODO(b/173337498): Should be absent, but horizontal class merging interferes with
+ // class inlining.
assertThat(
inspector.clazz("class_inliner_lambda_k_style.MainKt$testBigExtraMethod$1"),
- not(isPresent()));
- assertThat(
- inspector.clazz("class_inliner_lambda_k_style.MainKt$testBigExtraMethod2$1"),
- not(isPresent()));
- assertThat(
- inspector.clazz("class_inliner_lambda_k_style.MainKt$testBigExtraMethod3$1"),
- not(isPresent()));
-
- assertEquals(
- Sets.newHashSet(),
- collectAccessedTypes(lambdaCheck, clazz, "testBigExtraMethodReturningLambda"));
-
- assertThat(
- inspector.clazz(
- "class_inliner_lambda_k_style.MainKt$testBigExtraMethodReturningLambda$1"),
- not(isPresent()));
- assertThat(
- inspector.clazz(
- "class_inliner_lambda_k_style.MainKt$testBigExtraMethodReturningLambda2$1"),
- not(isPresent()));
- assertThat(
- inspector.clazz(
- "class_inliner_lambda_k_style.MainKt$testBigExtraMethodReturningLambda3$1"),
- not(isPresent()));
+ isPresent());
});
}
@Test
public void testDataClass() throws Exception {
- assumeTrue("Only work with -allowaccessmodification", allowAccessModification);
- final String mainClassName = "class_inliner_data_class.MainKt";
- runTestWithDefaults(
+ String mainClassName = "class_inliner_data_class.MainKt";
+ runTest(
"class_inliner_data_class",
mainClassName,
TestShrinkerBuilder::addDontWarnJetBrainsAnnotations)
.inspect(
inspector -> {
ClassSubject clazz = inspector.clazz(mainClassName);
- assertTrue(
+
+ // TODO(b/141719453): Data class should maybe be class inlined.
+ assertEquals(
+ Sets.newHashSet("class_inliner_data_class.Alpha"),
collectAccessedTypes(
- type -> !type.toSourceString().startsWith("java."),
- clazz,
- "main",
- String[].class.getCanonicalName())
- .isEmpty());
+ type -> !type.toSourceString().startsWith("java."),
+ clazz,
+ "main",
+ String[].class.getCanonicalName()));
assertEquals(
Lists.newArrayList(
"void kotlin.jvm.internal.Intrinsics.throwParameterIsNullException(java.lang.String)"),
@@ -325,39 +246,6 @@
.collect(Collectors.toSet());
}
- private R8TestRunResult runTestWithDefaults(String folder, String mainClass) throws Exception {
- return runTestWithDefaults(folder, mainClass, null);
- }
-
- private R8TestRunResult runTestWithDefaults(
- String folder, String mainClass, ThrowableConsumer<R8FullTestBuilder> configuration)
- throws Exception {
- return runTest(
- folder,
- mainClass,
- testBuilder ->
- testBuilder
- .addOptionsModification(
- options -> {
- options.enableInlining = true;
- options.enableLambdaMerging = false;
-
- // TODO(b/141719453): These limits should be removed if a possible or the test
- // refactored. Tests check if specific lambdas are inlined or not, where some
- // of target lambdas have at least 4 instructions.
- options.inliningInstructionLimit = 4;
- options.classInliningInstructionLimit = 40;
-
- // Class inlining depends on the processing order. We therefore insert all
- // call graph edges and verify that we can class inline everything under this
- // condition.
- options.testing.addCallEdgesForLibraryInvokes = true;
-
- options.horizontalClassMergerOptions().disableKotlinLambdaMerging();
- })
- .apply(configuration));
- }
-
private List<String> collectStaticCalls(ClassSubject clazz, String methodName, String... params) {
assertNotNull(clazz);
MethodSignature signature = new MethodSignature(methodName, "void", params);
diff --git a/src/test/java/com/android/tools/r8/kotlin/KotlinClassStaticizerTest.java b/src/test/java/com/android/tools/r8/kotlin/KotlinClassStaticizerTest.java
index e5d0d09..1598bb8 100644
--- a/src/test/java/com/android/tools/r8/kotlin/KotlinClassStaticizerTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/KotlinClassStaticizerTest.java
@@ -4,16 +4,14 @@
package com.android.tools.r8.kotlin;
-import static com.android.tools.r8.ToolHelper.getKotlinCompilers;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
import static org.hamcrest.CoreMatchers.not;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.Assert.assertTrue;
import static org.junit.Assume.assumeTrue;
-import com.android.tools.r8.KotlinCompilerTool.KotlinCompiler;
+import com.android.tools.r8.KotlinTestParameters;
import com.android.tools.r8.R8TestRunResult;
-import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
import com.android.tools.r8.utils.BooleanUtils;
import com.android.tools.r8.utils.codeinspector.ClassSubject;
import com.android.tools.r8.utils.codeinspector.FoundMethodSubject;
@@ -26,15 +24,16 @@
@RunWith(Parameterized.class)
public class KotlinClassStaticizerTest extends AbstractR8KotlinTestBase {
- @Parameterized.Parameters(name = "target: {0}, kotlinc: {1}, allowAccessModification: {2}")
+ @Parameterized.Parameters(name = "{0}, allowAccessModification: {1}")
public static Collection<Object[]> data() {
return buildParameters(
- KotlinTargetVersion.values(), getKotlinCompilers(), BooleanUtils.values());
+ getKotlinTestParameters().withAllCompilersAndTargetVersions().build(),
+ BooleanUtils.values());
}
public KotlinClassStaticizerTest(
- KotlinTargetVersion targetVersion, KotlinCompiler kotlinc, boolean allowAccessModification) {
- super(targetVersion, kotlinc, allowAccessModification);
+ KotlinTestParameters kotlinParameters, boolean allowAccessModification) {
+ super(kotlinParameters, allowAccessModification);
}
@Test
diff --git a/src/test/java/com/android/tools/r8/kotlin/KotlinDuplicateAnnotationTest.java b/src/test/java/com/android/tools/r8/kotlin/KotlinDuplicateAnnotationTest.java
index 7407d46..e74d082 100644
--- a/src/test/java/com/android/tools/r8/kotlin/KotlinDuplicateAnnotationTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/KotlinDuplicateAnnotationTest.java
@@ -3,16 +3,14 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.kotlin;
-import static com.android.tools.r8.ToolHelper.getKotlinCompilers;
import static org.hamcrest.CoreMatchers.containsString;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.Assert.fail;
import static org.junit.Assume.assumeTrue;
import com.android.tools.r8.CompilationFailedException;
-import com.android.tools.r8.KotlinCompilerTool.KotlinCompiler;
+import com.android.tools.r8.KotlinTestParameters;
import com.android.tools.r8.TestParameters;
-import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
import com.android.tools.r8.utils.BooleanUtils;
import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.StringUtils;
@@ -36,12 +34,11 @@
o.enableInlining = false;
};
- @Parameterized.Parameters(name = "{0} target: {1}, kotlinc: {2}, allowAccessModification: {3}")
+ @Parameterized.Parameters(name = "{0}, {1}, allowAccessModification: {2}")
public static Collection<Object[]> data() {
return buildParameters(
getTestParameters().withAllRuntimes().build(),
- KotlinTargetVersion.values(),
- getKotlinCompilers(),
+ getKotlinTestParameters().withAllCompilersAndTargetVersions().build(),
BooleanUtils.values());
}
@@ -49,10 +46,9 @@
public KotlinDuplicateAnnotationTest(
TestParameters parameters,
- KotlinTargetVersion targetVersion,
- KotlinCompiler kotlinc,
+ KotlinTestParameters kotlinParameters,
boolean allowAccessModification) {
- super(targetVersion, kotlinc, allowAccessModification);
+ super(kotlinParameters, allowAccessModification);
this.parameters = parameters;
}
diff --git a/src/test/java/com/android/tools/r8/kotlin/KotlinIntrinsicsInlineTest.java b/src/test/java/com/android/tools/r8/kotlin/KotlinIntrinsicsInlineTest.java
index eb1ca57..dae8a7d 100644
--- a/src/test/java/com/android/tools/r8/kotlin/KotlinIntrinsicsInlineTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/KotlinIntrinsicsInlineTest.java
@@ -3,15 +3,13 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.kotlin;
-import static com.android.tools.r8.ToolHelper.getKotlinCompilers;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.Assert.assertEquals;
import static org.junit.Assume.assumeTrue;
-import com.android.tools.r8.KotlinCompilerTool.KotlinCompiler;
+import com.android.tools.r8.KotlinTestParameters;
import com.android.tools.r8.TestParameters;
-import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
import com.android.tools.r8.utils.BooleanUtils;
import com.android.tools.r8.utils.StringUtils;
import com.android.tools.r8.utils.codeinspector.ClassSubject;
@@ -26,12 +24,11 @@
private static final String FOLDER = "intrinsics";
private static final String MAIN = FOLDER + ".InlineKt";
- @Parameterized.Parameters(name = "{0} target: {1}, kotlinc: {2}, allowAccessModification: {3}")
+ @Parameterized.Parameters(name = "{0}, {1}, allowAccessModification: {2}")
public static Collection<Object[]> data() {
return buildParameters(
getTestParameters().withAllRuntimes().build(),
- KotlinTargetVersion.values(),
- getKotlinCompilers(),
+ getKotlinTestParameters().withAllCompilersAndTargetVersions().build(),
BooleanUtils.values());
}
@@ -39,10 +36,9 @@
public KotlinIntrinsicsInlineTest(
TestParameters parameters,
- KotlinTargetVersion targetVersion,
- KotlinCompiler kotlinc,
+ KotlinTestParameters kotlinParameters,
boolean allowAccessModification) {
- super(targetVersion, kotlinc, allowAccessModification);
+ super(kotlinParameters, allowAccessModification);
this.parameters = parameters;
}
diff --git a/src/test/java/com/android/tools/r8/kotlin/KotlinUnusedArgumentsInLambdasTest.java b/src/test/java/com/android/tools/r8/kotlin/KotlinUnusedArgumentsInLambdasTest.java
index 5e25b92..fa3b505 100644
--- a/src/test/java/com/android/tools/r8/kotlin/KotlinUnusedArgumentsInLambdasTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/KotlinUnusedArgumentsInLambdasTest.java
@@ -3,15 +3,13 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.kotlin;
-import static com.android.tools.r8.ToolHelper.getKotlinCompilers;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
-import com.android.tools.r8.KotlinCompilerTool.KotlinCompiler;
+import com.android.tools.r8.KotlinTestParameters;
import com.android.tools.r8.TestShrinkerBuilder;
-import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
import com.android.tools.r8.utils.BooleanUtils;
import com.android.tools.r8.utils.codeinspector.MethodSubject;
import com.google.common.collect.ImmutableList;
@@ -23,15 +21,16 @@
@RunWith(Parameterized.class)
public class KotlinUnusedArgumentsInLambdasTest extends AbstractR8KotlinTestBase {
- @Parameterized.Parameters(name = "target: {0}, kotlinc: {1}, allowAccessModification: {2}")
+ @Parameterized.Parameters(name = "{0}, allowAccessModification: {1}")
public static Collection<Object[]> data() {
return buildParameters(
- KotlinTargetVersion.values(), getKotlinCompilers(), BooleanUtils.values());
+ getKotlinTestParameters().withAllCompilersAndTargetVersions().build(),
+ BooleanUtils.values());
}
public KotlinUnusedArgumentsInLambdasTest(
- KotlinTargetVersion targetVersion, KotlinCompiler kotlinc, boolean allowAccessModification) {
- super(targetVersion, kotlinc, allowAccessModification);
+ KotlinTestParameters kotlinParameters, boolean allowAccessModification) {
+ super(kotlinParameters, allowAccessModification);
}
@Test
diff --git a/src/test/java/com/android/tools/r8/kotlin/KotlinUnusedSingletonTest.java b/src/test/java/com/android/tools/r8/kotlin/KotlinUnusedSingletonTest.java
index 6a8e196..226fb8b 100644
--- a/src/test/java/com/android/tools/r8/kotlin/KotlinUnusedSingletonTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/KotlinUnusedSingletonTest.java
@@ -3,16 +3,14 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.kotlin;
-import static com.android.tools.r8.ToolHelper.getKotlinCompilers;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
import static org.hamcrest.CoreMatchers.not;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
-import com.android.tools.r8.KotlinCompilerTool.KotlinCompiler;
+import com.android.tools.r8.KotlinTestParameters;
import com.android.tools.r8.TestShrinkerBuilder;
-import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
import com.android.tools.r8.utils.BooleanUtils;
import com.android.tools.r8.utils.codeinspector.ClassSubject;
import com.android.tools.r8.utils.codeinspector.FieldSubject;
@@ -28,18 +26,19 @@
@RunWith(Parameterized.class)
public class KotlinUnusedSingletonTest extends AbstractR8KotlinTestBase {
- @Parameterized.Parameters(name = "target: {0}, kotlinc: {1}, allowAccessModification: {1}")
+ @Parameterized.Parameters(name = "{0}, allowAccessModification: {1}")
public static Collection<Object[]> data() {
return buildParameters(
- KotlinTargetVersion.values(), getKotlinCompilers(), BooleanUtils.values());
+ getKotlinTestParameters().withAllCompilersAndTargetVersions().build(),
+ BooleanUtils.values());
}
private static final String printlnSignature =
"void java.io.PrintStream.println(java.lang.Object)";
public KotlinUnusedSingletonTest(
- KotlinTargetVersion targetVersion, KotlinCompiler kotlinc, boolean allowAccessModification) {
- super(targetVersion, kotlinc, allowAccessModification);
+ KotlinTestParameters kotlinParameters, boolean allowAccessModification) {
+ super(kotlinParameters, allowAccessModification);
}
@Test
diff --git a/src/test/java/com/android/tools/r8/kotlin/ProcessKotlinReflectionLibTest.java b/src/test/java/com/android/tools/r8/kotlin/ProcessKotlinReflectionLibTest.java
index 3137d61..9f92a95 100644
--- a/src/test/java/com/android/tools/r8/kotlin/ProcessKotlinReflectionLibTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/ProcessKotlinReflectionLibTest.java
@@ -3,16 +3,14 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.kotlin;
-import static com.android.tools.r8.ToolHelper.getKotlinCompilers;
-import com.android.tools.r8.KotlinCompilerTool.KotlinCompiler;
import com.android.tools.r8.KotlinTestBase;
+import com.android.tools.r8.KotlinTestParameters;
import com.android.tools.r8.R8FullTestBuilder;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestShrinkerBuilder;
import com.android.tools.r8.ThrowableConsumer;
import com.android.tools.r8.ToolHelper;
-import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
import com.android.tools.r8.shaking.ProguardKeepAttributes;
import java.util.Collection;
import org.junit.Test;
@@ -24,17 +22,16 @@
private final TestParameters parameters;
- @Parameterized.Parameters(name = "{0} target: {1}")
+ @Parameterized.Parameters(name = "{0}, {1}")
public static Collection<Object[]> data() {
return buildParameters(
getTestParameters().withAllRuntimes().build(),
- KotlinTargetVersion.values(),
- getKotlinCompilers());
+ getKotlinTestParameters().withAllCompilersAndTargetVersions().build());
}
public ProcessKotlinReflectionLibTest(
- TestParameters parameters, KotlinTargetVersion targetVersion, KotlinCompiler kotlinc) {
- super(targetVersion, kotlinc);
+ TestParameters parameters, KotlinTestParameters kotlinParameters) {
+ super(kotlinParameters);
this.parameters = parameters;
}
diff --git a/src/test/java/com/android/tools/r8/kotlin/ProcessKotlinStdlibTest.java b/src/test/java/com/android/tools/r8/kotlin/ProcessKotlinStdlibTest.java
index 7166aed..c2c68e4 100644
--- a/src/test/java/com/android/tools/r8/kotlin/ProcessKotlinStdlibTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/ProcessKotlinStdlibTest.java
@@ -3,16 +3,14 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.kotlin;
-import static com.android.tools.r8.ToolHelper.getKotlinCompilers;
-import com.android.tools.r8.KotlinCompilerTool.KotlinCompiler;
import com.android.tools.r8.KotlinTestBase;
+import com.android.tools.r8.KotlinTestParameters;
import com.android.tools.r8.R8FullTestBuilder;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestShrinkerBuilder;
import com.android.tools.r8.ThrowableConsumer;
import com.android.tools.r8.ToolHelper;
-import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
import com.android.tools.r8.shaking.ProguardKeepAttributes;
import com.google.common.collect.ImmutableList;
import java.util.Collection;
@@ -24,17 +22,15 @@
public class ProcessKotlinStdlibTest extends KotlinTestBase {
private final TestParameters parameters;
- @Parameterized.Parameters(name = "{0} target: {1}")
+ @Parameterized.Parameters(name = "{0}, {1}")
public static Collection<Object[]> data() {
return buildParameters(
getTestParameters().withAllRuntimes().build(),
- KotlinTargetVersion.values(),
- getKotlinCompilers());
+ getKotlinTestParameters().withAllCompilersAndTargetVersions().build());
}
- public ProcessKotlinStdlibTest(
- TestParameters parameters, KotlinTargetVersion targetVersion, KotlinCompiler kotlinc) {
- super(targetVersion, kotlinc);
+ public ProcessKotlinStdlibTest(TestParameters parameters, KotlinTestParameters kotlinParameters) {
+ super(kotlinParameters);
this.parameters = parameters;
}
@@ -71,22 +67,6 @@
}
@Test
- public void testDontShrinkAndDontOptimizeDifferently() throws Exception {
- test(
- ImmutableList.of("-keep,allowobfuscation class **.*Exception*"),
- tb ->
- tb.addDontWarnJetBrainsAnnotations()
- .noTreeShaking()
- .addOptionsModification(
- o -> {
- // Randomly choose a couple of optimizations.
- o.enableClassInlining = false;
- o.enableLambdaMerging = false;
- o.enableValuePropagation = false;
- }));
- }
-
- @Test
public void testDontShrinkAndDontObfuscate() throws Exception {
test(
ImmutableList.of("-dontshrink", "-dontobfuscate"),
@@ -99,13 +79,6 @@
}
@Test
- public void testDontShrinkDifferently() throws Exception {
- test(
- ImmutableList.of("-keep,allowobfuscation class **.*Exception*"),
- tb -> tb.addDontWarnJetBrainsAnnotations().noTreeShaking());
- }
-
- @Test
public void testDontOptimize() throws Exception {
test(ImmutableList.of("-dontoptimize"));
}
diff --git a/src/test/java/com/android/tools/r8/kotlin/R8KotlinAccessorTest.java b/src/test/java/com/android/tools/r8/kotlin/R8KotlinAccessorTest.java
index 882bc22..a405400 100644
--- a/src/test/java/com/android/tools/r8/kotlin/R8KotlinAccessorTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/R8KotlinAccessorTest.java
@@ -4,16 +4,14 @@
package com.android.tools.r8.kotlin;
-import static com.android.tools.r8.ToolHelper.getKotlinCompilers;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
-import com.android.tools.r8.KotlinCompilerTool.KotlinCompiler;
+import com.android.tools.r8.KotlinTestParameters;
import com.android.tools.r8.R8TestBuilder;
import com.android.tools.r8.TestShrinkerBuilder;
import com.android.tools.r8.ToolHelper;
-import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
import com.android.tools.r8.ToolHelper.ProcessResult;
import com.android.tools.r8.jasmin.JasminBuilder;
import com.android.tools.r8.jasmin.JasminBuilder.ClassBuilder;
@@ -68,15 +66,16 @@
.addProperty("property", JAVA_LANG_STRING, Visibility.PRIVATE)
.addProperty("indirectPropertyGetter", JAVA_LANG_STRING, Visibility.PRIVATE);
- @Parameterized.Parameters(name = "target: {0}, kotlinc: {1}, allowAccessModification: {2}")
+ @Parameterized.Parameters(name = "{0}, allowAccessModification: {1}")
public static Collection<Object[]> data() {
return buildParameters(
- KotlinTargetVersion.values(), getKotlinCompilers(), BooleanUtils.values());
+ getKotlinTestParameters().withAllCompilersAndTargetVersions().build(),
+ BooleanUtils.values());
}
public R8KotlinAccessorTest(
- KotlinTargetVersion targetVersion, KotlinCompiler kotlinc, boolean allowAccessModification) {
- super(targetVersion, kotlinc, allowAccessModification);
+ KotlinTestParameters kotlinParameters, boolean allowAccessModification) {
+ super(kotlinParameters, allowAccessModification);
}
@Test
diff --git a/src/test/java/com/android/tools/r8/kotlin/R8KotlinDataClassTest.java b/src/test/java/com/android/tools/r8/kotlin/R8KotlinDataClassTest.java
index 3faf486..b2f0927 100644
--- a/src/test/java/com/android/tools/r8/kotlin/R8KotlinDataClassTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/R8KotlinDataClassTest.java
@@ -4,10 +4,7 @@
package com.android.tools.r8.kotlin;
-import static com.android.tools.r8.ToolHelper.getKotlinCompilers;
-
-import com.android.tools.r8.KotlinCompilerTool.KotlinCompiler;
-import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
+import com.android.tools.r8.KotlinTestParameters;
import com.android.tools.r8.graph.DexCode;
import com.android.tools.r8.kotlin.TestKotlinClass.Visibility;
import com.android.tools.r8.naming.MemberNaming.MethodSignature;
@@ -45,15 +42,16 @@
private Consumer<InternalOptions> disableClassInliner = o -> o.enableClassInlining = false;
- @Parameterized.Parameters(name = "target: {0}, kotlinc: {2}, allowAccessModification: {1}")
+ @Parameterized.Parameters(name = "{0}, allowAccessModification: {1}")
public static Collection<Object[]> data() {
return buildParameters(
- KotlinTargetVersion.values(), getKotlinCompilers(), BooleanUtils.values());
+ getKotlinTestParameters().withAllCompilersAndTargetVersions().build(),
+ BooleanUtils.values());
}
public R8KotlinDataClassTest(
- KotlinTargetVersion targetVersion, KotlinCompiler kotlinc, boolean allowAccessModification) {
- super(targetVersion, kotlinc, allowAccessModification);
+ KotlinTestParameters kotlinParameters, boolean allowAccessModification) {
+ super(kotlinParameters, allowAccessModification);
}
@Test
diff --git a/src/test/java/com/android/tools/r8/kotlin/R8KotlinIntrinsicsTest.java b/src/test/java/com/android/tools/r8/kotlin/R8KotlinIntrinsicsTest.java
index 934ee16..11d1ea2 100644
--- a/src/test/java/com/android/tools/r8/kotlin/R8KotlinIntrinsicsTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/R8KotlinIntrinsicsTest.java
@@ -4,10 +4,7 @@
package com.android.tools.r8.kotlin;
-import static com.android.tools.r8.ToolHelper.getKotlinCompilers;
-
-import com.android.tools.r8.KotlinCompilerTool.KotlinCompiler;
-import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
+import com.android.tools.r8.KotlinTestParameters;
import com.android.tools.r8.naming.MemberNaming.MethodSignature;
import com.android.tools.r8.utils.BooleanUtils;
import com.android.tools.r8.utils.codeinspector.ClassSubject;
@@ -26,15 +23,16 @@
private static final TestKotlinDataClass KOTLIN_INTRINSICS_CLASS =
new TestKotlinDataClass("kotlin.jvm.internal.Intrinsics");
- @Parameterized.Parameters(name = "target: {0}, kotlinc: {1}, allowAccessModification: {2}")
+ @Parameterized.Parameters(name = "{0}, allowAccessModification: {1}")
public static Collection<Object[]> data() {
return buildParameters(
- KotlinTargetVersion.values(), getKotlinCompilers(), BooleanUtils.values());
+ getKotlinTestParameters().withAllCompilersAndTargetVersions().build(),
+ BooleanUtils.values());
}
public R8KotlinIntrinsicsTest(
- KotlinTargetVersion targetVersion, KotlinCompiler kotlinc, boolean allowAccessModification) {
- super(targetVersion, kotlinc, allowAccessModification);
+ KotlinTestParameters kotlinParameters, boolean allowAccessModification) {
+ super(kotlinParameters, allowAccessModification);
}
@Test
diff --git a/src/test/java/com/android/tools/r8/kotlin/R8KotlinPropertiesTest.java b/src/test/java/com/android/tools/r8/kotlin/R8KotlinPropertiesTest.java
index b9fefbaf4..edd2c8f 100644
--- a/src/test/java/com/android/tools/r8/kotlin/R8KotlinPropertiesTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/R8KotlinPropertiesTest.java
@@ -4,11 +4,9 @@
package com.android.tools.r8.kotlin;
-import static com.android.tools.r8.ToolHelper.getKotlinCompilers;
import static org.junit.Assert.assertTrue;
-import com.android.tools.r8.KotlinCompilerTool.KotlinCompiler;
-import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
+import com.android.tools.r8.KotlinTestParameters;
import com.android.tools.r8.kotlin.TestKotlinClass.Visibility;
import com.android.tools.r8.naming.MemberNaming;
import com.android.tools.r8.naming.MemberNaming.MethodSignature;
@@ -95,15 +93,16 @@
o.enableClassStaticizer = false;
};
- @Parameterized.Parameters(name = "target: {0}, kotlinc: {1}, allowAccessModification: {1}")
+ @Parameterized.Parameters(name = "{0}, allowAccessModification: {1}")
public static Collection<Object[]> data() {
return buildParameters(
- KotlinTargetVersion.values(), getKotlinCompilers(), BooleanUtils.values());
+ getKotlinTestParameters().withAllCompilersAndTargetVersions().build(),
+ BooleanUtils.values());
}
public R8KotlinPropertiesTest(
- KotlinTargetVersion targetVersion, KotlinCompiler kotlinc, boolean allowAccessModification) {
- super(targetVersion, kotlinc, allowAccessModification);
+ KotlinTestParameters kotlinParameters, boolean allowAccessModification) {
+ super(kotlinParameters, allowAccessModification);
}
@Test
diff --git a/src/test/java/com/android/tools/r8/kotlin/SimplifyIfNotNullKotlinTest.java b/src/test/java/com/android/tools/r8/kotlin/SimplifyIfNotNullKotlinTest.java
index 64e3a80..c9fd417 100644
--- a/src/test/java/com/android/tools/r8/kotlin/SimplifyIfNotNullKotlinTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/SimplifyIfNotNullKotlinTest.java
@@ -3,11 +3,9 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.kotlin;
-import static com.android.tools.r8.ToolHelper.getKotlinCompilers;
import static org.junit.Assert.assertEquals;
-import com.android.tools.r8.KotlinCompilerTool.KotlinCompiler;
-import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
+import com.android.tools.r8.KotlinTestParameters;
import com.android.tools.r8.naming.MemberNaming.MethodSignature;
import com.android.tools.r8.utils.BooleanUtils;
import com.android.tools.r8.utils.codeinspector.ClassSubject;
@@ -23,15 +21,16 @@
private static final String FOLDER = "non_null";
private static final String STRING = "java.lang.String";
- @Parameterized.Parameters(name = "target: {0}, kotlinc: {1}, allowAccessModification: {2}")
+ @Parameterized.Parameters(name = "{0}, allowAccessModification: {1}")
public static Collection<Object[]> data() {
return buildParameters(
- KotlinTargetVersion.values(), getKotlinCompilers(), BooleanUtils.values());
+ getKotlinTestParameters().withAllCompilersAndTargetVersions().build(),
+ BooleanUtils.values());
}
public SimplifyIfNotNullKotlinTest(
- KotlinTargetVersion targetVersion, KotlinCompiler kotlinc, boolean allowAccessModification) {
- super(targetVersion, kotlinc, allowAccessModification);
+ KotlinTestParameters kotlinParameters, boolean allowAccessModification) {
+ super(kotlinParameters, allowAccessModification);
}
@Test
diff --git a/src/test/java/com/android/tools/r8/kotlin/coroutines/KotlinxCoroutinesTestRunner.java b/src/test/java/com/android/tools/r8/kotlin/coroutines/KotlinxCoroutinesTestRunner.java
index 4a05b76..1241446 100644
--- a/src/test/java/com/android/tools/r8/kotlin/coroutines/KotlinxCoroutinesTestRunner.java
+++ b/src/test/java/com/android/tools/r8/kotlin/coroutines/KotlinxCoroutinesTestRunner.java
@@ -4,13 +4,11 @@
package com.android.tools.r8.kotlin.coroutines;
-import static com.android.tools.r8.ToolHelper.getKotlinCompilers;
import static org.junit.Assert.assertEquals;
-import com.android.tools.r8.KotlinCompilerTool.KotlinCompiler;
+import com.android.tools.r8.KotlinTestParameters;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.ToolHelper;
-import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
import com.android.tools.r8.ToolHelper.ProcessResult;
import com.android.tools.r8.kotlin.metadata.KotlinMetadataTestBase;
import com.android.tools.r8.utils.ZipUtils;
@@ -50,19 +48,18 @@
private Set<String> notWorkingTests =
Sets.newHashSet("kotlinx.coroutines.test.TestDispatchersTest");
- @Parameterized.Parameters(name = "{0}, target: {1}, kotlinc: {2}")
+ @Parameterized.Parameters(name = "{0}, {1}")
public static Collection<Object[]> data() {
return buildParameters(
getTestParameters().withCfRuntimes().build(),
- KotlinTargetVersion.values(),
- getKotlinCompilers());
+ getKotlinTestParameters().withAllCompilersAndTargetVersions().build());
}
private final TestParameters parameters;
public KotlinxCoroutinesTestRunner(
- TestParameters parameters, KotlinTargetVersion targetVersion, KotlinCompiler kotlinc) {
- super(targetVersion, kotlinc);
+ TestParameters parameters, KotlinTestParameters kotlinParameters) {
+ super(kotlinParameters);
this.parameters = parameters;
}
diff --git a/src/test/java/com/android/tools/r8/kotlin/lambda/JStyleKotlinLambdaMergingWithEnumUnboxingTest.java b/src/test/java/com/android/tools/r8/kotlin/lambda/JStyleKotlinLambdaMergingWithEnumUnboxingTest.java
index 7103667..787ae1f 100644
--- a/src/test/java/com/android/tools/r8/kotlin/lambda/JStyleKotlinLambdaMergingWithEnumUnboxingTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/lambda/JStyleKotlinLambdaMergingWithEnumUnboxingTest.java
@@ -9,13 +9,9 @@
import com.android.tools.r8.KotlinCompilerTool.KotlinCompiler;
import com.android.tools.r8.NeverClassInline;
import com.android.tools.r8.NeverInline;
-import com.android.tools.r8.NoHorizontalClassMerging;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.ToolHelper;
-import com.android.tools.r8.graph.DexProgramClass;
-import com.android.tools.r8.ir.optimize.lambda.kotlin.JStyleLambdaGroupIdFactory;
-import com.android.tools.r8.ir.optimize.lambda.kotlin.KotlinLambdaGroupIdFactory;
import com.android.tools.r8.kotlin.lambda.JStyleKotlinLambdaMergingWithEnumUnboxingTest.Main.EnumUnboxingCandidate;
import java.util.List;
import org.junit.Test;
@@ -49,31 +45,17 @@
.addDefaultRuntimeLibrary(parameters)
.addLibraryFiles(ToolHelper.getKotlinStdlibJar(kotlinc))
.addKeepMainRule(Main.class)
- .addOptionsModification(
- options ->
- options.testing.kotlinLambdaMergerFactoryForClass =
- this::getKotlinLambdaMergerFactoryForClass)
- .addHorizontallyMergedLambdaClassesInspector(
- inspector -> inspector.assertMerged(Lambda1.class, Lambda2.class))
+ .addHorizontallyMergedClassesInspector(
+ inspector -> inspector.assertMergedInto(Lambda2.class, Lambda1.class))
.addEnumUnboxingInspector(inspector -> inspector.assertUnboxed(EnumUnboxingCandidate.class))
.enableInliningAnnotations()
.enableNeverClassInliningAnnotations()
- .enableNoHorizontalClassMergingAnnotations()
.setMinApi(parameters.getApiLevel())
.compile()
.run(parameters.getRuntime(), Main.class)
.assertSuccessWithOutputLines("Lambda1.method()", "Lambda2.method()");
}
- private KotlinLambdaGroupIdFactory getKotlinLambdaMergerFactoryForClass(DexProgramClass clazz) {
- String typeName = clazz.getType().toSourceString();
- if (typeName.equals(Lambda1.class.getTypeName())
- || typeName.equals(Lambda2.class.getTypeName())) {
- return JStyleLambdaGroupIdFactory.getInstance();
- }
- return null;
- }
-
static class Main {
@NeverClassInline
@@ -121,7 +103,6 @@
}
@NeverClassInline
- @NoHorizontalClassMerging
public static final class Lambda2 implements I {
@NeverInline
diff --git a/src/test/java/com/android/tools/r8/kotlin/lambda/KStyleKotlinLambdaMergingWithEnumUnboxingTest.java b/src/test/java/com/android/tools/r8/kotlin/lambda/KStyleKotlinLambdaMergingWithEnumUnboxingTest.java
index aea188c..fe0ef77 100644
--- a/src/test/java/com/android/tools/r8/kotlin/lambda/KStyleKotlinLambdaMergingWithEnumUnboxingTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/lambda/KStyleKotlinLambdaMergingWithEnumUnboxingTest.java
@@ -9,13 +9,9 @@
import com.android.tools.r8.KotlinCompilerTool.KotlinCompiler;
import com.android.tools.r8.NeverClassInline;
import com.android.tools.r8.NeverInline;
-import com.android.tools.r8.NoHorizontalClassMerging;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.ToolHelper;
-import com.android.tools.r8.graph.DexProgramClass;
-import com.android.tools.r8.ir.optimize.lambda.kotlin.KStyleLambdaGroupIdFactory;
-import com.android.tools.r8.ir.optimize.lambda.kotlin.KotlinLambdaGroupIdFactory;
import com.android.tools.r8.kotlin.lambda.KStyleKotlinLambdaMergingWithEnumUnboxingTest.Main.EnumUnboxingCandidate;
import java.util.List;
import org.junit.Test;
@@ -48,17 +44,12 @@
.addInnerClasses(getClass())
.addProgramFiles(ToolHelper.getKotlinStdlibJar(kotlinc))
.addKeepMainRule(Main.class)
- .addOptionsModification(
- options ->
- options.testing.kotlinLambdaMergerFactoryForClass =
- this::getKotlinLambdaMergerFactoryForClass)
- .addHorizontallyMergedLambdaClassesInspector(
- inspector -> inspector.assertMerged(Lambda1.class, Lambda2.class))
+ .addHorizontallyMergedClassesInspector(
+ inspector -> inspector.assertMergedInto(Lambda2.class, Lambda1.class))
.addEnumUnboxingInspector(inspector -> inspector.assertUnboxed(EnumUnboxingCandidate.class))
.addDontWarnJetBrainsNotNullAnnotation()
.enableInliningAnnotations()
.enableNeverClassInliningAnnotations()
- .enableNoHorizontalClassMergingAnnotations()
.noMinification()
.setMinApi(parameters.getApiLevel())
.compile()
@@ -66,15 +57,6 @@
.assertSuccessWithOutputLines("Lambda1.method()", "Lambda2.method()");
}
- private KotlinLambdaGroupIdFactory getKotlinLambdaMergerFactoryForClass(DexProgramClass clazz) {
- String typeName = clazz.getType().toSourceString();
- if (typeName.equals(Lambda1.class.getTypeName())
- || typeName.equals(Lambda2.class.getTypeName())) {
- return KStyleLambdaGroupIdFactory.getInstance();
- }
- return null;
- }
-
static class Main {
@NeverClassInline
@@ -123,7 +105,6 @@
}
@NeverClassInline
- @NoHorizontalClassMerging
public static final class Lambda2 extends kotlin.jvm.internal.Lambda<kotlin.Unit>
implements kotlin.jvm.functions.Function0<kotlin.Unit> {
diff --git a/src/test/java/com/android/tools/r8/kotlin/lambda/KotlinLambdaMergerValidationTest.java b/src/test/java/com/android/tools/r8/kotlin/lambda/KotlinLambdaMergerValidationTest.java
index 4f78df2..655ddb5 100644
--- a/src/test/java/com/android/tools/r8/kotlin/lambda/KotlinLambdaMergerValidationTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/lambda/KotlinLambdaMergerValidationTest.java
@@ -3,16 +3,14 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.kotlin.lambda;
-import static com.android.tools.r8.ToolHelper.getKotlinCompilers;
import static org.hamcrest.CoreMatchers.equalTo;
import static org.junit.Assume.assumeTrue;
-import com.android.tools.r8.KotlinCompilerTool.KotlinCompiler;
+import com.android.tools.r8.KotlinTestParameters;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestRuntime;
import com.android.tools.r8.TestRuntime.CfRuntime;
import com.android.tools.r8.ToolHelper;
-import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
import com.android.tools.r8.kotlin.AbstractR8KotlinTestBase;
import com.android.tools.r8.utils.DescriptorUtils;
import java.nio.file.Path;
@@ -26,17 +24,16 @@
private final TestParameters parameters;
- @Parameterized.Parameters(name = "{0}, target: {1}, kotlinc: {2}")
+ @Parameterized.Parameters(name = "{0}, {1}")
public static Collection<Object[]> data() {
return buildParameters(
getTestParameters().withAllRuntimesAndApiLevels().build(),
- KotlinTargetVersion.values(),
- getKotlinCompilers());
+ getKotlinTestParameters().withAllCompilersAndTargetVersions().build());
}
public KotlinLambdaMergerValidationTest(
- TestParameters parameters, KotlinTargetVersion targetVersion, KotlinCompiler kotlinc) {
- super(targetVersion, kotlinc, false);
+ TestParameters parameters, KotlinTestParameters kotlinParameters) {
+ super(kotlinParameters, false);
this.parameters = parameters;
}
diff --git a/src/test/java/com/android/tools/r8/kotlin/lambda/KotlinLambdaMergingCapturesKotlinStyleTest.java b/src/test/java/com/android/tools/r8/kotlin/lambda/KotlinLambdaMergingCapturesKotlinStyleTest.java
new file mode 100644
index 0000000..0a49461
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/kotlin/lambda/KotlinLambdaMergingCapturesKotlinStyleTest.java
@@ -0,0 +1,161 @@
+// 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.kotlin.lambda;
+
+import static com.android.tools.r8.utils.PredicateUtils.not;
+import static junit.framework.TestCase.assertEquals;
+import static org.hamcrest.CoreMatchers.containsString;
+import static org.junit.Assume.assumeFalse;
+import static org.junit.Assume.assumeTrue;
+
+import com.android.tools.r8.KotlinTestBase;
+import com.android.tools.r8.KotlinTestParameters;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestRuntime.CfVm;
+import com.android.tools.r8.ToolHelper.DexVm.Version;
+import com.android.tools.r8.references.ClassReference;
+import com.android.tools.r8.utils.BooleanUtils;
+import com.android.tools.r8.utils.StringUtils;
+import com.android.tools.r8.utils.codeinspector.HorizontallyMergedClassesInspector;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
+import java.io.IOException;
+import java.nio.file.Path;
+import java.util.Collection;
+import java.util.List;
+import java.util.Set;
+import java.util.stream.Collectors;
+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 KotlinLambdaMergingCapturesKotlinStyleTest extends KotlinTestBase {
+
+ private final boolean allowAccessModification;
+ private final TestParameters parameters;
+
+ @Parameters(name = "{0}, {1}, allow access modification: {2}")
+ public static Collection<Object[]> data() {
+ return buildParameters(
+ getTestParameters()
+ .withCfRuntime(CfVm.last())
+ .withDexRuntime(Version.last())
+ .withAllApiLevels()
+ .build(),
+ getKotlinTestParameters().withAllCompilersAndTargetVersions().build(),
+ BooleanUtils.values());
+ }
+
+ public KotlinLambdaMergingCapturesKotlinStyleTest(
+ TestParameters parameters,
+ KotlinTestParameters kotlinParameters,
+ boolean allowAccessModification) {
+ super(kotlinParameters);
+ this.allowAccessModification = allowAccessModification;
+ this.parameters = parameters;
+ }
+
+ @Test
+ public void testJVM() throws Exception {
+ assumeFalse(allowAccessModification);
+ assumeTrue(parameters.isCfRuntime());
+ assumeTrue(kotlinParameters.isFirst());
+ testForJvm()
+ .addProgramFiles(getProgramFiles())
+ .run(parameters.getRuntime(), getMainClassName())
+ .assertSuccessWithOutput(getExpectedOutput());
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ testForR8(parameters.getBackend())
+ .addProgramFiles(getProgramFiles())
+ .addKeepMainRule(getMainClassName())
+ .addDontWarnJetBrainsAnnotations()
+ .addHorizontallyMergedClassesInspector(this::inspect)
+ .allowAccessModification(allowAccessModification)
+ .allowDiagnosticWarningMessages()
+ .setMinApi(parameters.getApiLevel())
+ .compile()
+ .assertAllWarningMessagesMatch(
+ containsString("Resource 'META-INF/MANIFEST.MF' already exists."))
+ .run(parameters.getRuntime(), getMainClassName())
+ .assertSuccessWithOutput(getExpectedOutput());
+ }
+
+ private void inspect(HorizontallyMergedClassesInspector inspector) throws IOException {
+ // Get the Kotlin lambdas in the input.
+ KotlinLambdasInInput lambdasInInput =
+ KotlinLambdasInInput.create(getProgramFiles(), getTestName());
+ assertEquals(0, lambdasInInput.getNumberOfJStyleLambdas());
+ assertEquals(26, lambdasInInput.getNumberOfKStyleLambdas());
+
+ // Only a subset of all K-style Kotlin lambdas are merged.
+ Set<ClassReference> unmergedLambdas =
+ ImmutableSet.of(
+ lambdasInInput.getKStyleLambdaReferenceFromTypeName(getTestName(), "MainKt$test1$15"),
+ lambdasInInput.getKStyleLambdaReferenceFromTypeName(getTestName(), "MainKt$test2$9"),
+ lambdasInInput.getKStyleLambdaReferenceFromTypeName(getTestName(), "MainKt$test2$10"),
+ lambdasInInput.getKStyleLambdaReferenceFromTypeName(getTestName(), "MainKt$test2$11"));
+ inspector
+ .assertClassReferencesMerged(
+ lambdasInInput.getKStyleLambdas().stream()
+ .filter(not(unmergedLambdas::contains))
+ .collect(Collectors.toList()))
+ .assertClassReferencesNotMerged(unmergedLambdas);
+ }
+
+ private String getExpectedOutput() {
+ return StringUtils.lines(
+ "a: 1 2 3",
+ "b: 2 3 1",
+ "c: 3 1 2",
+ "d: 1 A D(d=x)",
+ "e: 2 D(d=y) B",
+ "f: 3 D(d=z) D(d=x)",
+ "g: 7 D(d=z) 3",
+ "h: 8 9 1",
+ "i: A B C",
+ "j: D(d=x) D(d=y) D(d=z)",
+ "k: 7 8 9",
+ "l: A D(d=y) 9",
+ "n: 7 B D(d=z)",
+ "o: D(d=x) 8 C",
+ "p: 1 2 C",
+ "a: true 10 * 20 30 40 50.0 60.0 D(d=D) S null 70",
+ "a: true 10 D(d=D) S * 20 30 40 50.0 60.0 null 70",
+ "a: true * 20 40 50.0 60.0 S null 70 10 30 D(d=D)",
+ "a: D(d=D) S null 70 true 10 * 20 30 40 50.0 60.0",
+ "a: true 10 * 20 30 40 50.0 60.0 D(d=D) S $o3 $o4",
+ "a: true 10 * 20 30 40 50.0 60.0 D(d=D) $o2 $o3 70",
+ "a: true 10 * 20 30 40 50.0 60.0 $o1 $o2 null 70",
+ "a: true 10 * 20 30 40 50.0 60.0 $o1 S null $o4",
+ "x: true 10 * 20 30 40 50.0 60.0 D(d=D) S $o3 70",
+ "y: true 10 * 20 30 40 $f 60.0 D(d=D) S null 70",
+ "z: true 10 * $s 30 40 50.0 60.0 D(d=D) S null 70");
+ }
+
+ private Path getJavaJarFile() {
+ return getJavaJarFile(getTestName());
+ }
+
+ private String getMainClassName() {
+ return getTestName() + ".MainKt";
+ }
+
+ private List<Path> getProgramFiles() {
+ Path kotlinJarFile =
+ getCompileMemoizer(getKotlinFilesInResource(getTestName()), getTestName())
+ .configure(kotlinCompilerTool -> kotlinCompilerTool.includeRuntime().noReflect())
+ .getForConfiguration(kotlinc, targetVersion);
+ return ImmutableList.of(kotlinJarFile, getJavaJarFile());
+ }
+
+ private String getTestName() {
+ return "lambdas_kstyle_captures";
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/kotlin/lambda/KotlinLambdaMergingDebugTest.java b/src/test/java/com/android/tools/r8/kotlin/lambda/KotlinLambdaMergingDebugTest.java
index 9cee958..61f417e 100644
--- a/src/test/java/com/android/tools/r8/kotlin/lambda/KotlinLambdaMergingDebugTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/lambda/KotlinLambdaMergingDebugTest.java
@@ -3,11 +3,10 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.kotlin.lambda;
-import static com.android.tools.r8.ToolHelper.getKotlinCompilers;
import static org.hamcrest.CoreMatchers.equalTo;
import com.android.tools.r8.CompilationMode;
-import com.android.tools.r8.KotlinCompilerTool.KotlinCompiler;
+import com.android.tools.r8.KotlinTestParameters;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
import com.android.tools.r8.kotlin.AbstractR8KotlinTestBase;
@@ -24,14 +23,19 @@
private static final String FOLDER = "reprocess_merged_lambdas_kstyle";
private static final String MAIN_CLASS = "reprocess_merged_lambdas_kstyle.MainKt";
- @Parameters(name = "{0}")
+ @Parameters(name = "{0}, {1}")
public static List<Object[]> data() {
return buildParameters(
- getTestParameters().withDexRuntimes().withAllApiLevels().build(), getKotlinCompilers());
+ getTestParameters().withDexRuntimes().withAllApiLevels().build(),
+ getKotlinTestParameters()
+ .withAllCompilers()
+ .withTargetVersion(KotlinTargetVersion.JAVA_6)
+ .build());
}
- public KotlinLambdaMergingDebugTest(TestParameters parameters, KotlinCompiler kotlinc) {
- super(KotlinTargetVersion.JAVA_6, kotlinc);
+ public KotlinLambdaMergingDebugTest(
+ TestParameters parameters, KotlinTestParameters kotlinParameters) {
+ super(kotlinParameters);
this.parameters = parameters;
}
diff --git a/src/test/java/com/android/tools/r8/kotlin/lambda/KotlinLambdaMergingKeepAttributesKotlinStyleTest.java b/src/test/java/com/android/tools/r8/kotlin/lambda/KotlinLambdaMergingKeepAttributesKotlinStyleTest.java
new file mode 100644
index 0000000..a20a77d
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/kotlin/lambda/KotlinLambdaMergingKeepAttributesKotlinStyleTest.java
@@ -0,0 +1,160 @@
+// 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.kotlin.lambda;
+
+import static com.android.tools.r8.shaking.ProguardKeepAttributes.ENCLOSING_METHOD;
+import static com.android.tools.r8.shaking.ProguardKeepAttributes.INNER_CLASSES;
+import static com.android.tools.r8.shaking.ProguardKeepAttributes.SIGNATURE;
+import static junit.framework.TestCase.assertEquals;
+import static org.hamcrest.CoreMatchers.containsString;
+import static org.junit.Assume.assumeFalse;
+import static org.junit.Assume.assumeTrue;
+
+import com.android.tools.r8.KotlinTestBase;
+import com.android.tools.r8.KotlinTestParameters;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestRuntime.CfVm;
+import com.android.tools.r8.ToolHelper.DexVm.Version;
+import com.android.tools.r8.utils.BooleanUtils;
+import com.android.tools.r8.utils.StringUtils;
+import com.android.tools.r8.utils.codeinspector.HorizontallyMergedClassesInspector;
+import com.google.common.collect.ImmutableList;
+import java.io.IOException;
+import java.nio.file.Path;
+import java.util.Collection;
+import java.util.Collections;
+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 KotlinLambdaMergingKeepAttributesKotlinStyleTest extends KotlinTestBase {
+
+ private final boolean allowAccessModification;
+ private final List<String> attributes;
+ private final TestParameters parameters;
+
+ @Parameters(name = "{0}, {1}, allow access modification: {2}, attributes: {3}")
+ public static Collection<Object[]> data() {
+ return buildParameters(
+ getTestParameters()
+ .withCfRuntime(CfVm.last())
+ .withDexRuntime(Version.last())
+ .withAllApiLevels()
+ .build(),
+ getKotlinTestParameters().withAllCompilersAndTargetVersions().build(),
+ BooleanUtils.values(),
+ ImmutableList.of(
+ Collections.emptyList(),
+ ImmutableList.of(ENCLOSING_METHOD, INNER_CLASSES),
+ ImmutableList.of(ENCLOSING_METHOD, INNER_CLASSES, SIGNATURE)));
+ }
+
+ public KotlinLambdaMergingKeepAttributesKotlinStyleTest(
+ TestParameters parameters,
+ KotlinTestParameters kotlinParameters,
+ boolean allowAccessModification,
+ List<String> attributes) {
+ super(kotlinParameters);
+ this.allowAccessModification = allowAccessModification;
+ this.attributes = attributes;
+ this.parameters = parameters;
+ }
+
+ @Test
+ public void testJVM() throws Exception {
+ assumeFalse(allowAccessModification);
+ assumeTrue(parameters.isCfRuntime());
+ assumeTrue(kotlinParameters.isFirst());
+ testForJvm()
+ .addProgramFiles(getProgramFiles())
+ .run(parameters.getRuntime(), getMainClassName())
+ .assertSuccessWithOutput(getExpectedOutput());
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ testForR8(parameters.getBackend())
+ .addProgramFiles(getProgramFiles())
+ .addKeepMainRule(getMainClassName())
+ .applyIf(!attributes.isEmpty(), builder -> builder.addKeepAttributes(attributes))
+ .addDontWarnJetBrainsAnnotations()
+ .addHorizontallyMergedClassesInspector(this::inspect)
+ .allowAccessModification(allowAccessModification)
+ .allowDiagnosticWarningMessages()
+ .setMinApi(parameters.getApiLevel())
+ .compile()
+ .assertAllWarningMessagesMatch(
+ containsString("Resource 'META-INF/MANIFEST.MF' already exists."))
+ .run(parameters.getRuntime(), getMainClassName())
+ .assertSuccessWithOutput(getExpectedOutput());
+ }
+
+ private void inspect(HorizontallyMergedClassesInspector inspector) throws IOException {
+ // Get the Kotlin lambdas in the input.
+ KotlinLambdasInInput lambdasInInput =
+ KotlinLambdasInInput.create(getProgramFiles(), getTestName());
+ assertEquals(0, lambdasInInput.getNumberOfJStyleLambdas());
+ assertEquals(24, lambdasInInput.getNumberOfKStyleLambdas());
+
+ // All K-style Kotlin lambdas are merged if no attributes are kept.
+ if (attributes.isEmpty()) {
+ inspector.assertClassReferencesMerged(lambdasInInput.getKStyleLambdas());
+ } else {
+ // TODO(b/179018501): allow merging classes with inner/outer classes.
+ inspector.assertClassReferencesNotMerged(lambdasInInput.getKStyleLambdas());
+ }
+ }
+
+ private String getExpectedOutput() {
+ return StringUtils.lines(
+ "Alpha(id=11)",
+ "Beta(id=12)",
+ "Gamma(payload={any}, id=13)",
+ "Alpha(id=14)",
+ "First-1-Beta(id=15)",
+ "First-2-Beta(id=16)",
+ "First-3-Beta(id=17)",
+ "First-A-Gamma(payload=18, id=19)-11",
+ "First-B-Gamma(payload=20, id=21)-11",
+ "First-C-Gamma(payload=22, id=23)-11",
+ "First-D-Gamma(payload=24, id=25)-11",
+ "First-E-Gamma(payload=26, id=27)-11",
+ "First-F-Gamma(payload=28, id=29)-11",
+ "Second-1-Beta(id=30)",
+ "Second-2-Beta(id=31)",
+ "Second-3-Beta(id=32)",
+ "Second-A-Gamma(payload=33, id=34)-22",
+ "Second-B-Gamma(payload=35, id=36)-22",
+ "Second-C-Gamma(payload=37, id=38)-22",
+ "Second-D-Gamma(payload=39, id=40)-22",
+ "Second-E-Gamma(payload=41, id=42)-22",
+ "Second-F-Gamma(payload=43, id=44)-22",
+ "4321 45 46 47",
+ "1234 Alpha(id=48) Beta(id=49) Gamma(payload=50, id=51)");
+ }
+
+ private Path getJavaJarFile() {
+ return getJavaJarFile(getTestName());
+ }
+
+ private String getMainClassName() {
+ return getTestName() + ".MainKt";
+ }
+
+ private List<Path> getProgramFiles() {
+ Path kotlinJarFile =
+ getCompileMemoizer(getKotlinFilesInResource(getTestName()), getTestName())
+ .configure(kotlinCompilerTool -> kotlinCompilerTool.includeRuntime().noReflect())
+ .getForConfiguration(kotlinc, targetVersion);
+ return ImmutableList.of(kotlinJarFile, getJavaJarFile());
+ }
+
+ private String getTestName() {
+ return "lambdas_kstyle_generics";
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/kotlin/lambda/KotlinLambdaMergingSingletonTest.java b/src/test/java/com/android/tools/r8/kotlin/lambda/KotlinLambdaMergingSingletonTest.java
new file mode 100644
index 0000000..72abfe3
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/kotlin/lambda/KotlinLambdaMergingSingletonTest.java
@@ -0,0 +1,155 @@
+// 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.kotlin.lambda;
+
+import static com.android.tools.r8.utils.PredicateUtils.not;
+import static junit.framework.TestCase.assertEquals;
+import static org.hamcrest.CoreMatchers.containsString;
+import static org.junit.Assume.assumeFalse;
+import static org.junit.Assume.assumeTrue;
+
+import com.android.tools.r8.KotlinTestBase;
+import com.android.tools.r8.KotlinTestParameters;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestRuntime.CfVm;
+import com.android.tools.r8.ToolHelper.DexVm.Version;
+import com.android.tools.r8.references.ClassReference;
+import com.android.tools.r8.utils.BooleanUtils;
+import com.android.tools.r8.utils.StringUtils;
+import com.android.tools.r8.utils.codeinspector.HorizontallyMergedClassesInspector;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Sets;
+import java.io.IOException;
+import java.nio.file.Path;
+import java.util.Collection;
+import java.util.List;
+import java.util.Set;
+import java.util.stream.Collectors;
+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 KotlinLambdaMergingSingletonTest extends KotlinTestBase {
+
+ private final boolean allowAccessModification;
+ private final TestParameters parameters;
+
+ @Parameters(name = "{0}, {1}, allow access modification: {2}")
+ public static Collection<Object[]> data() {
+ return buildParameters(
+ getTestParameters()
+ .withCfRuntime(CfVm.last())
+ .withDexRuntime(Version.last())
+ .withAllApiLevels()
+ .build(),
+ getKotlinTestParameters().withAllCompilersAndTargetVersions().build(),
+ BooleanUtils.values());
+ }
+
+ public KotlinLambdaMergingSingletonTest(
+ TestParameters parameters,
+ KotlinTestParameters kotlinParameters,
+ boolean allowAccessModification) {
+ super(kotlinParameters);
+ this.allowAccessModification = allowAccessModification;
+ this.parameters = parameters;
+ }
+
+ @Test
+ public void testJVM() throws Exception {
+ assumeFalse(allowAccessModification);
+ assumeTrue(parameters.isCfRuntime());
+ assumeTrue(kotlinParameters.isFirst());
+ testForJvm()
+ .addProgramFiles(getProgramFiles())
+ .run(parameters.getRuntime(), getMainClassName())
+ .assertSuccessWithOutput(getExpectedOutput());
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ testForR8(parameters.getBackend())
+ .addProgramFiles(getProgramFiles())
+ .addKeepMainRule(getMainClassName())
+ .addDontWarnJetBrainsAnnotations()
+ .addHorizontallyMergedClassesInspector(this::inspect)
+ .allowAccessModification(allowAccessModification)
+ .allowDiagnosticWarningMessages()
+ .setMinApi(parameters.getApiLevel())
+ .compile()
+ .assertAllWarningMessagesMatch(
+ containsString("Resource 'META-INF/MANIFEST.MF' already exists."))
+ .run(parameters.getRuntime(), getMainClassName())
+ .assertSuccessWithOutput(getExpectedOutput());
+ }
+
+ private void inspect(HorizontallyMergedClassesInspector inspector) throws IOException {
+ // Get the Kotlin lambdas in the input.
+ KotlinLambdasInInput lambdasInInput =
+ KotlinLambdasInInput.create(getProgramFiles(), getTestName());
+ assertEquals(2, lambdasInInput.getNumberOfJStyleLambdas());
+ assertEquals(7, lambdasInInput.getNumberOfKStyleLambdas());
+
+ // All J-style Kotlin lambdas should be merged into one class.
+ inspector.assertIsCompleteMergeGroup(lambdasInInput.getJStyleLambdas());
+
+ // A subset of the K-style Kotlin lambdas should be merged into one class.
+ Set<ClassReference> kStyleLambdaMergeGroup =
+ Sets.newHashSet(
+ lambdasInInput.getKStyleLambdaReferenceFromTypeName(
+ getTestName(), "MainKt$test2$$inlined$process$1"),
+ lambdasInInput.getKStyleLambdaReferenceFromTypeName(
+ getTestName(), "MainKt$test2$$inlined$process$2"),
+ lambdasInInput.getKStyleLambdaReferenceFromTypeName(
+ getTestName(), "MainKt$test2$$inlined$process$3"),
+ lambdasInInput.getKStyleLambdaReferenceFromTypeName(
+ getTestName(), "MainKt$test2$$inlined$process$4"));
+ if (allowAccessModification) {
+ kStyleLambdaMergeGroup.add(
+ lambdasInInput.getKStyleLambdaReferenceFromTypeName(
+ getTestName(), "MainKt$test2$lambda$1"));
+ }
+ inspector.assertIsCompleteMergeGroup(kStyleLambdaMergeGroup);
+
+ // The remaining lambdas are not merged.
+ inspector.assertClassReferencesNotMerged(
+ lambdasInInput.getAllLambdas().stream()
+ .filter(not(lambdasInInput::isJStyleLambda))
+ .filter(not(kStyleLambdaMergeGroup::contains))
+ .collect(Collectors.toSet()));
+ }
+
+ private String getExpectedOutput() {
+ return StringUtils.lines(
+ "(*000*001*002*003*004*005*006*007*008*009*)",
+ "(*000*001*002*003*004*005*006*007*008*009*)",
+ "(*010*011*)",
+ "(*012*013*014*)",
+ "(*015*016*)",
+ "(*017*018*019*)");
+ }
+
+ private Path getJavaJarFile() {
+ return getJavaJarFile(getTestName());
+ }
+
+ private String getMainClassName() {
+ return getTestName() + ".MainKt";
+ }
+
+ private List<Path> getProgramFiles() {
+ Path kotlinJarFile =
+ getCompileMemoizer(getKotlinFilesInResource(getTestName()), getTestName())
+ .configure(kotlinCompilerTool -> kotlinCompilerTool.includeRuntime().noReflect())
+ .getForConfiguration(kotlinc, targetVersion);
+ return ImmutableList.of(kotlinJarFile, getJavaJarFile());
+ }
+
+ private String getTestName() {
+ return "lambdas_singleton";
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/kotlin/lambda/KotlinLambdaMergingTest.java b/src/test/java/com/android/tools/r8/kotlin/lambda/KotlinLambdaMergingTest.java
deleted file mode 100644
index 4997fe3..0000000
--- a/src/test/java/com/android/tools/r8/kotlin/lambda/KotlinLambdaMergingTest.java
+++ /dev/null
@@ -1,580 +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.kotlin.lambda;
-
-import static com.android.tools.r8.ToolHelper.getKotlinCompilers;
-import static com.google.common.base.Predicates.alwaysTrue;
-import static junit.framework.TestCase.assertEquals;
-import static junit.framework.TestCase.assertTrue;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.fail;
-
-import com.android.tools.r8.KotlinCompilerTool.KotlinCompiler;
-import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
-import com.android.tools.r8.graph.DexClass;
-import com.android.tools.r8.graph.DexType;
-import com.android.tools.r8.ir.optimize.lambda.CaptureSignature;
-import com.android.tools.r8.kotlin.AbstractR8KotlinTestBase;
-import com.android.tools.r8.utils.BooleanUtils;
-import com.android.tools.r8.utils.InternalOptions;
-import com.android.tools.r8.utils.codeinspector.CodeInspector;
-import com.google.common.collect.Lists;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.List;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.Parameterized;
-
-@RunWith(Parameterized.class)
-public class KotlinLambdaMergingTest extends AbstractR8KotlinTestBase {
- private static final String KOTLIN_FUNCTION_IFACE = "Lkotlin/jvm/functions/Function";
- private static final String KOTLIN_FUNCTION_IFACE_STR = "kotlin.jvm.functions.Function";
-
- private void configure(InternalOptions options) {
- options.enableClassInlining = false;
- // The test checks that the generated lambdas inherit from Function, which is not true if
- // the unused interface removal is enabled.
- options.enableUnusedInterfaceRemoval = enableUnusedInterfaceRemoval;
- // Ensure that enclosing method and inner class attributes are kept even on classes that are
- // not explicitly mentioned by a keep rule.
- options.forceProguardCompatibility = true;
- options.horizontalClassMergerOptions().disableKotlinLambdaMerging();
- }
-
- private final boolean enableUnusedInterfaceRemoval;
-
- @Parameterized.Parameters(
- name =
- "target: {0}, kotlinc: {1}, allow access modification: {2}, unused interface removal:"
- + " {3}")
- public static Collection<Object[]> data() {
- return buildParameters(
- KotlinTargetVersion.values(),
- getKotlinCompilers(),
- BooleanUtils.values(),
- BooleanUtils.values());
- }
-
- public KotlinLambdaMergingTest(
- KotlinTargetVersion targetVersion,
- KotlinCompiler kotlinc,
- boolean allowAccessModification,
- boolean enableUnusedInterfaceRemoval) {
- super(targetVersion, kotlinc, allowAccessModification);
- this.enableUnusedInterfaceRemoval = enableUnusedInterfaceRemoval;
- }
-
- abstract static class LambdaOrGroup {
- abstract boolean match(DexClass clazz);
- }
-
- static class Group extends LambdaOrGroup {
- final String pkg;
- final String capture;
- final int arity;
- final String sam;
- final int singletons;
-
- private Group(String pkg, String capture, int arity, String sam, int singletons) {
- this.pkg = pkg;
- this.capture = fixCapture(capture);
- this.arity = arity;
- this.sam = sam;
- this.singletons = singletons;
- }
-
- private String fixCapture(String capture) {
- capture += "I";
- char[] chars = capture.toCharArray();
- Arrays.sort(chars);
- return new String(chars);
- }
-
- @Override
- public String toString() {
- return "group class " +
- (pkg.length() == 0 ? "" : pkg + "/") +
- "-$$LambdaGroup$XXXX (arity: " + arity +
- ", capture: " + capture + ", iface: " + sam + ", sing: " + singletons + ")";
- }
-
- @Override
- boolean match(DexClass clazz) {
- return clazz.type.getPackageDescriptor().equals(pkg) &&
- getLambdaOrGroupCapture(clazz).equals(capture) &&
- getLambdaSam(clazz).equals(sam) &&
- getLambdaSingletons(clazz) == singletons &&
- getLambdaOrGroupArity(clazz) == arity;
- }
- }
-
- private static Group kstyleImpl(String pkg, String capture, int arity, int singletons) {
- return new Group(pkg, capture, arity, KOTLIN_FUNCTION_IFACE_STR + arity, singletons);
- }
-
- static Group kstyle(String pkg, int arity) {
- return kstyleImpl(pkg, "", arity, 0);
- }
-
- static Group kstyle(String pkg, int arity, int singletons) {
- assertTrue(singletons != 0);
- return kstyleImpl(pkg, "", arity, singletons);
- }
-
- private static Group kstyle(String pkg, String capture, int arity) {
- assertFalse(capture.isEmpty());
- return kstyleImpl(pkg, capture, arity, 0);
- }
-
- private static Group jstyleImpl(
- String pkg, String capture, int arity, String sam, int singletons) {
- assertTrue(capture.isEmpty() || singletons == 0);
- return new Group(pkg, capture, arity, sam, singletons);
- }
-
- private static Group jstyle(String pkg, String capture, int arity, String sam) {
- return jstyleImpl(pkg, capture, arity, sam, 0);
- }
-
- private static Group jstyle(String pkg, int arity, String sam, int singletons) {
- return jstyleImpl(pkg, "", arity, sam, singletons);
- }
-
- static class Lambda extends LambdaOrGroup {
- final String pkg;
- final String name;
- final int arity;
-
- Lambda(String pkg, String name, int arity) {
- this.pkg = pkg;
- this.name = name;
- this.arity = arity;
- }
-
- @Override
- public String toString() {
- return "lambda class " +
- (pkg.length() == 0 ? "" : pkg + "/") +
- name + " (arity: " + arity + ")";
- }
-
- @Override
- boolean match(DexClass clazz) {
- return clazz.type.getPackageDescriptor().equals(pkg) &&
- clazz.type.getName().equals(name) &&
- getLambdaOrGroupArity(clazz) == arity;
- }
- }
-
- static class Verifier {
- final CodeInspector codeInspector;
- final List<DexClass> lambdas = new ArrayList<>();
- final List<DexClass> groups = new ArrayList<>();
-
- Verifier(CodeInspector codeInspector) {
- this.codeInspector = codeInspector;
- initGroupsAndLambdas();
- }
-
- private void initGroupsAndLambdas() {
- codeInspector.forAllClasses(
- clazz -> {
- DexClass dexClass = clazz.getDexProgramClass();
- if (isLambdaOrGroup(dexClass)) {
- if (isLambdaGroupClass(dexClass)) {
- groups.add(dexClass);
- } else {
- lambdas.add(dexClass);
- }
- }
- });
- }
-
- void assertLambdaGroups(Group... groups) {
- assertLambdasOrGroups("Lambda group", this.groups, groups);
- }
-
- void assertLambdas(Lambda... lambdas) {
- assertLambdasOrGroups("Lambda", this.lambdas, lambdas);
- }
-
- @SafeVarargs
- private static <T extends LambdaOrGroup>
- void assertLambdasOrGroups(String what, List<DexClass> objects, T... checks) {
- ArrayList<DexClass> list = Lists.newArrayList(objects);
- for (int i = 0; i < checks.length; i++) {
- T check = checks[i];
- for (DexClass clazz : list) {
- if (check.match(clazz)) {
- // Validate static initializer.
- if (check instanceof Group) {
- assertEquals(
- clazz.getMethodCollection().numberOfDirectMethods(),
- ((Group) check).singletons == 0 ? 1 : 2);
- }
-
- list.remove(clazz);
- checks[i] = null;
- break;
- }
- }
- }
-
- int notFound = 0;
- for (T check : checks) {
- if (check != null) {
- System.err.println(what + " not found: " + check);
- notFound++;
- }
- }
-
- for (DexClass dexClass : list) {
- System.err.println(what + " unexpected: " +
- dexClass.type.descriptor.toString() +
- ", arity: " + getLambdaOrGroupArity(dexClass) +
- ", capture: " + getLambdaOrGroupCapture(dexClass) +
- ", sam: " + getLambdaSam(dexClass) +
- ", sing: " + getLambdaSingletons(dexClass));
- notFound++;
- }
-
- assertTrue(what + "s match failed", 0 == notFound && 0 == list.size());
- }
- }
-
- private static int getLambdaOrGroupArity(DexClass clazz) {
- if (isKStyleLambdaOrGroup(clazz)) {
- for (DexType iface : clazz.interfaces.values) {
- String descr = iface.descriptor.toString();
- if (descr.startsWith(KOTLIN_FUNCTION_IFACE)) {
- return Integer.parseInt(
- descr.substring(KOTLIN_FUNCTION_IFACE.length(), descr.length() - 1));
- }
- }
-
- } else {
- assertTrue(isJStyleLambdaOrGroup(clazz));
- // Taking the number of any virtual method parameters seems to be good enough.
- assertTrue(clazz.getMethodCollection().hasVirtualMethods());
- return clazz.lookupVirtualMethod(alwaysTrue()).method.proto.parameters.size();
- }
- fail("Failed to get arity for " + clazz.type.descriptor.toString());
- throw new AssertionError();
- }
-
- private static String getLambdaSam(DexClass clazz) {
- assertEquals(1, clazz.interfaces.size());
- return clazz.interfaces.values[0].toSourceString();
- }
-
- private static int getLambdaSingletons(DexClass clazz) {
- assertEquals(1, clazz.interfaces.size());
- return clazz.staticFields().size();
- }
-
- private static boolean isLambdaOrGroup(DexClass clazz) {
- return !clazz.type.getPackageDescriptor().startsWith("kotlin") &&
- (isKStyleLambdaOrGroup(clazz) || isJStyleLambdaOrGroup(clazz));
- }
-
- private static boolean isKStyleLambdaOrGroup(DexClass clazz) {
- return clazz.superType.descriptor.toString().equals("Lkotlin/jvm/internal/Lambda;");
- }
-
- private static boolean isJStyleLambdaOrGroup(DexClass clazz) {
- return clazz.superType.descriptor.toString().equals("Ljava/lang/Object;") &&
- clazz.interfaces.size() == 1;
- }
-
- private static boolean isLambdaGroupClass(DexClass clazz) {
- return clazz.type.getName().startsWith("-$$LambdaGroup$");
- }
-
- private static String getLambdaOrGroupCapture(DexClass clazz) {
- return CaptureSignature.getCaptureSignature(clazz.instanceFields());
- }
-
- @Test
- public void testTrivialKs() throws Exception {
- final String mainClassName = "lambdas_kstyle_trivial.MainKt";
- runTest(
- "lambdas_kstyle_trivial",
- mainClassName,
- testBuilder ->
- testBuilder
- .addKeepRules(
- "-keepunusedarguments class * extends kotlin.jvm.internal.Lambda {"
- + " invoke(int, short); }")
- .addDontWarnJetBrainsNotNullAnnotation()
- .addOptionsModification(this::configure))
- .inspect(
- inspector -> {
- if (enableUnusedInterfaceRemoval) {
- // Only test that the code generates the same output as the input code does on the
- // JVM.
- return;
- }
-
- Verifier verifier = new Verifier(inspector);
- String pkg = "lambdas_kstyle_trivial";
-
- verifier.assertLambdaGroups(
- allowAccessModification
- ? new Group[] {
- kstyle("", 0, 4),
- kstyle("", 1, 9),
- kstyle("", 2, 2), // -\
- kstyle("", 2, 5), // - 3 groups different by main method
- kstyle("", 2, 4), // -/
- kstyle("", 3, 2),
- kstyle("", 22, 2)
- }
- : new Group[] {
- kstyle(pkg, 0, 2),
- kstyle(pkg, 1, 5),
- kstyle(pkg, 2, 5), // - 2 groups different by main method
- kstyle(pkg, 2, 4), // -/
- kstyle(pkg, 3, 2),
- kstyle(pkg, 22, 2),
- kstyle(pkg + "/inner", 0, 2),
- kstyle(pkg + "/inner", 1, 4)
- });
-
- verifier.assertLambdas(
- allowAccessModification
- ? new Lambda[] {}
- : new Lambda[] {
- new Lambda(pkg, "MainKt$testStateless$8", 2),
- new Lambda(pkg + "/inner", "InnerKt$testInnerStateless$7", 2)
- });
- });
- }
-
- @Test
- public void testCapturesKs() throws Exception {
- final String mainClassName = "lambdas_kstyle_captures.MainKt";
- runTest(
- "lambdas_kstyle_captures",
- mainClassName,
- testBuilder ->
- testBuilder
- .addDontWarnJetBrainsAnnotations()
- .addOptionsModification(this::configure))
- .inspect(
- inspector -> {
- if (enableUnusedInterfaceRemoval) {
- // Only test that the code generates the same output as the input code does on the
- // JVM.
- return;
- }
-
- Verifier verifier = new Verifier(inspector);
- String pkg = "lambdas_kstyle_captures";
- String grpPkg = allowAccessModification ? "" : pkg;
-
- verifier.assertLambdaGroups(
- kstyle(grpPkg, "LLL", 0),
- kstyle(grpPkg, "ILL", 0),
- kstyle(grpPkg, "III", 0),
- kstyle(grpPkg, "BCDFIJLLLLSZ", 0),
- kstyle(grpPkg, "BCDFIJLLSZ", 0));
-
- verifier.assertLambdas(
- new Lambda(pkg, "MainKt$test1$15", 0),
- new Lambda(pkg, "MainKt$test2$10", 0),
- new Lambda(pkg, "MainKt$test2$11", 0),
- new Lambda(pkg, "MainKt$test2$9", 0));
- });
- }
-
- @Test
- public void testGenericsNoSignatureKs() throws Exception {
- final String mainClassName = "lambdas_kstyle_generics.MainKt";
- runTest(
- "lambdas_kstyle_generics",
- mainClassName,
- testBuilder ->
- testBuilder
- .addDontWarnJetBrainsAnnotations()
- .addOptionsModification(this::configure))
- .inspect(
- inspector -> {
- if (enableUnusedInterfaceRemoval) {
- // Only test that the code generates the same output as the input code does on the
- // JVM.
- return;
- }
-
- Verifier verifier = new Verifier(inspector);
- String pkg = "lambdas_kstyle_generics";
- String grpPkg = allowAccessModification ? "" : pkg;
-
- verifier.assertLambdaGroups(
- kstyle(grpPkg, 1, 3), // Group for Any
- kstyle(grpPkg, "L", 1), // Group for Beta
- kstyle(grpPkg, "LS", 1), // Group for Gamma
- kstyle(grpPkg, 1, 2) // Group for int
- );
-
- verifier.assertLambdas(new Lambda(pkg, "MainKt$main$4", 1));
- });
- }
-
- @Test
- public void testInnerClassesAndEnclosingMethodsKs() throws Exception {
- final String mainClassName = "lambdas_kstyle_generics.MainKt";
- runTest(
- "lambdas_kstyle_generics",
- mainClassName,
- testBuilder ->
- testBuilder
- .addDontWarnJetBrainsAnnotations()
- .addKeepAttributeInnerClassesAndEnclosingMethod()
- .addOptionsModification(this::configure))
- .inspect(
- inspector -> {
- if (enableUnusedInterfaceRemoval) {
- // Only test that the code generates the same output as the input code does on the
- // JVM.
- return;
- }
-
- Verifier verifier = new Verifier(inspector);
- String pkg = "lambdas_kstyle_generics";
- String grpPkg = allowAccessModification ? "" : pkg;
-
- verifier.assertLambdaGroups(
- kstyle(grpPkg, 1, 3), // Group for Any
- kstyle(grpPkg, "L", 1), // Group for Beta // First
- kstyle(grpPkg, "L", 1), // Group for Beta // Second
- kstyle(grpPkg, "LS", 1), // Group for Gamma // First
- kstyle(grpPkg, "LS", 1), // Group for Gamma // Second
- kstyle(grpPkg, 1, 2) // Group for int
- );
-
- verifier.assertLambdas(new Lambda(pkg, "MainKt$main$4", 1));
- });
- }
-
- @Test
- public void testGenericsSignatureInnerEnclosingKs() throws Exception {
- final String mainClassName = "lambdas_kstyle_generics.MainKt";
- runTest(
- "lambdas_kstyle_generics",
- mainClassName,
- // KEEP_SIGNATURE_INNER_ENCLOSING,
- testBuilder ->
- testBuilder
- .addDontWarnJetBrainsAnnotations()
- .addKeepAttributeInnerClassesAndEnclosingMethod()
- .addKeepAttributeSignature()
- .addOptionsModification(this::configure))
- .inspect(
- inspector -> {
- if (enableUnusedInterfaceRemoval) {
- // Only test that the code generates the same output as the input code does on the
- // JVM.
- return;
- }
-
- Verifier verifier = new Verifier(inspector);
- String pkg = "lambdas_kstyle_generics";
- String grpPkg = allowAccessModification ? "" : pkg;
-
- verifier.assertLambdaGroups(
- kstyle(grpPkg, 1, 3), // Group for Any
- kstyle(grpPkg, "L", 1), // Group for Beta in First
- kstyle(grpPkg, "L", 1), // Group for Beta in Second
- kstyle(grpPkg, "LS", 1), // Group for Gamma<String> in First
- kstyle(grpPkg, "LS", 1), // Group for Gamma<Integer> in First
- kstyle(grpPkg, "LS", 1), // Group for Gamma<String> in Second
- kstyle(grpPkg, "LS", 1), // Group for Gamma<Integer> in Second
- kstyle(grpPkg, 1, 2) // Group for int
- );
-
- verifier.assertLambdas(new Lambda(pkg, "MainKt$main$4", 1));
- });
- }
-
- @Test
- public void testTrivialJs() throws Exception {
- final String mainClassName = "lambdas_jstyle_trivial.MainKt";
- runTest(
- "lambdas_jstyle_trivial",
- mainClassName,
- testBuilder ->
- testBuilder
- .addDontWarnJetBrainsAnnotations()
- .addOptionsModification(this::configure))
- .inspect(
- inspector -> {
- Verifier verifier = new Verifier(inspector);
- String pkg = "lambdas_jstyle_trivial";
- String grp = allowAccessModification ? "" : pkg;
-
- String supplier = "lambdas_jstyle_trivial.Lambdas$Supplier";
- String intSupplier = "lambdas_jstyle_trivial.Lambdas$IntSupplier";
- String consumer = "lambdas_jstyle_trivial.Lambdas$Consumer";
- String intConsumer = "lambdas_jstyle_trivial.Lambdas$IntConsumer";
- String multiFunction = "lambdas_jstyle_trivial.Lambdas$MultiFunction";
-
- verifier.assertLambdaGroups(
- jstyle(grp, 0, intSupplier, 2),
- jstyle(grp, "L", 0, supplier),
- jstyle(grp, "LL", 0, supplier),
- jstyle(grp, "LLL", 0, supplier),
- jstyle(grp, 1, intConsumer, allowAccessModification ? 3 : 2),
- jstyle(grp, "I", 1, consumer),
- jstyle(grp, "II", 1, consumer),
- jstyle(grp, "III", 1, consumer),
- jstyle(grp, "IIII", 1, consumer),
- jstyle(grp, 3, multiFunction, 2),
- jstyle(grp, 3, multiFunction, 2),
- jstyle(grp, 3, multiFunction, 4),
- jstyle(grp, 3, multiFunction, 6));
-
- verifier.assertLambdas(
- allowAccessModification
- ? new Lambda[] {
- new Lambda(pkg + "/inner", "InnerKt$testInner1$4", 1),
- new Lambda(pkg + "/inner", "InnerKt$testInner1$5", 1)
- }
- : new Lambda[] {
- new Lambda(pkg + "/inner", "InnerKt$testInner1$1", 1),
- new Lambda(pkg + "/inner", "InnerKt$testInner1$2", 1),
- new Lambda(pkg + "/inner", "InnerKt$testInner1$3", 1),
- new Lambda(pkg + "/inner", "InnerKt$testInner1$4", 1),
- new Lambda(pkg + "/inner", "InnerKt$testInner1$5", 1)
- });
- });
- }
-
- @Test
- public void testSingleton() throws Exception {
- final String mainClassName = "lambdas_singleton.MainKt";
- runTest(
- "lambdas_singleton",
- mainClassName,
- testBuilder ->
- testBuilder
- .addDontWarnJetBrainsAnnotations()
- .addOptionsModification(this::configure)
- .noHorizontalClassMerging())
- .inspect(
- inspector -> {
- Verifier verifier = new Verifier(inspector);
- String pkg = "lambdas_singleton";
- String grp = allowAccessModification ? "" : pkg;
-
- verifier.assertLambdaGroups(
- kstyle(grp, 1 /* 1 out of 5 lambdas in the group */),
- jstyle(grp, 2, "java.util.Comparator", 0 /* 0 out of 2 lambdas in the group */));
-
- verifier.assertLambdas(/* None */ );
- });
- }
-}
diff --git a/src/test/java/com/android/tools/r8/kotlin/lambda/KotlinLambdaMergingTrivialJavaStyleTest.java b/src/test/java/com/android/tools/r8/kotlin/lambda/KotlinLambdaMergingTrivialJavaStyleTest.java
new file mode 100644
index 0000000..1e83c00
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/kotlin/lambda/KotlinLambdaMergingTrivialJavaStyleTest.java
@@ -0,0 +1,210 @@
+// 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.kotlin.lambda;
+
+import static com.android.tools.r8.utils.PredicateUtils.not;
+import static junit.framework.TestCase.assertEquals;
+import static org.hamcrest.CoreMatchers.containsString;
+import static org.junit.Assume.assumeFalse;
+import static org.junit.Assume.assumeTrue;
+
+import com.android.tools.r8.KotlinTestBase;
+import com.android.tools.r8.KotlinTestParameters;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestRuntime.CfVm;
+import com.android.tools.r8.ToolHelper.DexVm.Version;
+import com.android.tools.r8.references.ClassReference;
+import com.android.tools.r8.utils.BooleanUtils;
+import com.android.tools.r8.utils.StringUtils;
+import com.android.tools.r8.utils.codeinspector.HorizontallyMergedClassesInspector;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
+import java.io.IOException;
+import java.nio.file.Path;
+import java.util.Collection;
+import java.util.List;
+import java.util.Set;
+import java.util.stream.Collectors;
+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 KotlinLambdaMergingTrivialJavaStyleTest extends KotlinTestBase {
+
+ private final boolean allowAccessModification;
+ private final TestParameters parameters;
+
+ @Parameters(name = "{0}, {1}, allow access modification: {2}")
+ public static Collection<Object[]> data() {
+ return buildParameters(
+ getTestParameters()
+ .withCfRuntime(CfVm.last())
+ .withDexRuntime(Version.last())
+ .withAllApiLevels()
+ .build(),
+ getKotlinTestParameters().withAllCompilersAndTargetVersions().build(),
+ BooleanUtils.values());
+ }
+
+ public KotlinLambdaMergingTrivialJavaStyleTest(
+ TestParameters parameters,
+ KotlinTestParameters kotlinParameters,
+ boolean allowAccessModification) {
+ super(kotlinParameters);
+ this.allowAccessModification = allowAccessModification;
+ this.parameters = parameters;
+ }
+
+ @Test
+ public void testJVM() throws Exception {
+ assumeFalse(allowAccessModification);
+ assumeTrue(parameters.isCfRuntime());
+ assumeTrue(kotlinParameters.isFirst());
+ testForJvm()
+ .addProgramFiles(getProgramFiles())
+ .run(parameters.getRuntime(), getMainClassName())
+ .assertSuccessWithOutput(getExpectedOutput());
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ testForR8(parameters.getBackend())
+ .addProgramFiles(getProgramFiles())
+ .addKeepMainRule(getMainClassName())
+ .addDontWarnJetBrainsAnnotations()
+ .addHorizontallyMergedClassesInspector(this::inspect)
+ .allowAccessModification(allowAccessModification)
+ .allowDiagnosticWarningMessages()
+ .setMinApi(parameters.getApiLevel())
+ .compile()
+ .assertAllWarningMessagesMatch(
+ containsString("Resource 'META-INF/MANIFEST.MF' already exists."))
+ .run(parameters.getRuntime(), getMainClassName())
+ .assertSuccessWithOutput(getExpectedOutput());
+ }
+
+ private void inspect(HorizontallyMergedClassesInspector inspector) throws IOException {
+ // Get the Kotlin lambdas in the input.
+ KotlinLambdasInInput lambdasInInput =
+ KotlinLambdasInInput.create(getProgramFiles(), getTestName());
+ assertEquals(39, lambdasInInput.getNumberOfJStyleLambdas());
+ assertEquals(0, lambdasInInput.getNumberOfKStyleLambdas());
+
+ if (!allowAccessModification) {
+ // Only a subset of all J-style Kotlin lambdas are merged without -allowaccessmodification.
+ Set<ClassReference> unmergedLambdas =
+ ImmutableSet.of(
+ lambdasInInput.getJStyleLambdaReferenceFromTypeName(
+ getTestName(), "inner.InnerKt$testInner1$1"),
+ lambdasInInput.getJStyleLambdaReferenceFromTypeName(
+ getTestName(), "inner.InnerKt$testInner1$2"),
+ lambdasInInput.getJStyleLambdaReferenceFromTypeName(
+ getTestName(), "inner.InnerKt$testInner1$3"),
+ lambdasInInput.getJStyleLambdaReferenceFromTypeName(
+ getTestName(), "inner.InnerKt$testInner1$4"),
+ lambdasInInput.getJStyleLambdaReferenceFromTypeName(
+ getTestName(), "inner.InnerKt$testInner1$5"));
+ inspector
+ .assertClassReferencesMerged(
+ lambdasInInput.getJStyleLambdas().stream()
+ .filter(not(unmergedLambdas::contains))
+ .collect(Collectors.toList()))
+ .assertClassReferencesNotMerged(unmergedLambdas);
+ return;
+ }
+
+ // All J-style Kotlin lambdas are merged with -allowaccessmodification.
+ inspector.assertClassReferencesMerged(lambdasInInput.getJStyleLambdas());
+ }
+
+ private String getExpectedOutput() {
+ return StringUtils.lines(
+ "{005:4}",
+ "{007:6}",
+ "009:{008}:{0}",
+ "011:{010}:{0}",
+ "013:{012}:{0}:{1}",
+ "015:{014}:{0}:{1}",
+ "017:{Local(id=016)}:{0}:{1}:{2}",
+ "019:{Local(id=018)}:{0}:{1}:{2}",
+ "021:{Local(id=Local(id=020))}:{0}:{1}:{2}:{3}",
+ "023:{Local(id=Local(id=022))}:{0}:{1}:{2}:{3}",
+ "27",
+ "kotlin.Unit",
+ "28",
+ "kotlin.Unit",
+ "029:024",
+ "kotlin.Unit",
+ "030:024",
+ "kotlin.Unit",
+ "031:024",
+ "kotlin.Unit",
+ "032:024",
+ "kotlin.Unit",
+ "Local(id=033):024:025",
+ "kotlin.Unit",
+ "Local(id=034):024:025",
+ "kotlin.Unit",
+ "Local(id=Local(id=035)):024:025:026",
+ "kotlin.Unit",
+ "Local(id=Local(id=036)):024:025:026",
+ "kotlin.Unit",
+ "037:038:039",
+ "039:037:038",
+ "038:039:037",
+ "Local(id=037):038:039",
+ "Local(id=038):037:039",
+ "037:Local(id=038):039",
+ "037:Local(id=039):038",
+ "Local(id=Local(id=037)):Local(id=Local(id=038)):Local(id=Local(id=039))",
+ "Local(id=Local(id=039)):Local(id=Local(id=037)):Local(id=Local(id=038))",
+ "040:Local(id=041):Local(id=Local(id=042))",
+ "Local(id=Local(id=042)):040:Local(id=041)",
+ "Local(id=041):Local(id=Local(id=042)):040",
+ "Local(id=040):Local(id=041):Local(id=Local(id=042))",
+ "Local(id=Local(id=041)):040:Local(id=Local(id=042))",
+ "040:Local(id=Local(id=041)):Local(id=Local(id=042))",
+ "040:Local(id=Local(id=Local(id=042))):Local(id=041)",
+ "Local(id=Local(id=040)):Local(id=Local(id=Local(id=041))):Local(id=Local(id=Local(id=Local(id=042))))",
+ "Local(id=Local(id=Local(id=Local(id=042)))):Local(id=Local(id=040)):Local(id=Local(id=Local(id=041)))",
+ "043:044:045",
+ "045:043:044",
+ "044:045:043",
+ "Local(id=043):044:045",
+ "Local(id=044):043:045",
+ "046:Local(id=047):Local(id=048)",
+ "Local(id=048):046:Local(id=047)",
+ "Local(id=047):Local(id=048):046",
+ "Local(id=046):Local(id=047):Local(id=048)",
+ "Local(id=Local(id=047)):046:Local(id=048)",
+ "{053:100}",
+ "055:{054}:{49}",
+ "057:{056}:{49}:{50}",
+ "059:{InnerLocal(id=058)}:{49}:{50}:{51}",
+ "061:{InnerLocal(id=InnerLocal(id=060))}:{49}:{50}:{51}:{52");
+ }
+
+ private Path getJavaJarFile() {
+ return getJavaJarFile(getTestName());
+ }
+
+ private String getMainClassName() {
+ return getTestName() + ".MainKt";
+ }
+
+ private List<Path> getProgramFiles() {
+ Path kotlinJarFile =
+ getCompileMemoizer(getKotlinFilesInResource(getTestName()), getTestName())
+ .configure(kotlinCompilerTool -> kotlinCompilerTool.includeRuntime().noReflect())
+ .getForConfiguration(kotlinc, targetVersion);
+ return ImmutableList.of(kotlinJarFile, getJavaJarFile());
+ }
+
+ private String getTestName() {
+ return "lambdas_jstyle_trivial";
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/kotlin/lambda/KotlinLambdaMergingTrivialKotlinStyleTest.java b/src/test/java/com/android/tools/r8/kotlin/lambda/KotlinLambdaMergingTrivialKotlinStyleTest.java
new file mode 100644
index 0000000..3515109
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/kotlin/lambda/KotlinLambdaMergingTrivialKotlinStyleTest.java
@@ -0,0 +1,171 @@
+// 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.kotlin.lambda;
+
+import static com.android.tools.r8.utils.PredicateUtils.not;
+import static junit.framework.TestCase.assertEquals;
+import static org.hamcrest.CoreMatchers.containsString;
+import static org.junit.Assume.assumeFalse;
+import static org.junit.Assume.assumeTrue;
+
+import com.android.tools.r8.KotlinTestBase;
+import com.android.tools.r8.KotlinTestParameters;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestRuntime.CfVm;
+import com.android.tools.r8.ToolHelper.DexVm.Version;
+import com.android.tools.r8.references.ClassReference;
+import com.android.tools.r8.utils.BooleanUtils;
+import com.android.tools.r8.utils.StringUtils;
+import com.android.tools.r8.utils.codeinspector.HorizontallyMergedClassesInspector;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
+import java.io.IOException;
+import java.nio.file.Path;
+import java.util.Collection;
+import java.util.List;
+import java.util.Set;
+import java.util.stream.Collectors;
+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 KotlinLambdaMergingTrivialKotlinStyleTest extends KotlinTestBase {
+
+ private final boolean allowAccessModification;
+ private final TestParameters parameters;
+
+ @Parameters(name = "{0}, {1}, allow access modification: {2}")
+ public static Collection<Object[]> data() {
+ return buildParameters(
+ getTestParameters()
+ .withCfRuntime(CfVm.last())
+ .withDexRuntime(Version.last())
+ .withAllApiLevels()
+ .build(),
+ getKotlinTestParameters().withAllCompilersAndTargetVersions().build(),
+ BooleanUtils.values());
+ }
+
+ public KotlinLambdaMergingTrivialKotlinStyleTest(
+ TestParameters parameters,
+ KotlinTestParameters kotlinParameters,
+ boolean allowAccessModification) {
+ super(kotlinParameters);
+ this.allowAccessModification = allowAccessModification;
+ this.parameters = parameters;
+ }
+
+ @Test
+ public void testJVM() throws Exception {
+ assumeFalse(allowAccessModification);
+ assumeTrue(parameters.isCfRuntime());
+ assumeTrue(kotlinParameters.isFirst());
+ testForJvm()
+ .addProgramFiles(getProgramFiles())
+ .run(parameters.getRuntime(), getMainClassName())
+ .assertSuccessWithOutput(getExpectedOutput());
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ testForR8(parameters.getBackend())
+ .addProgramFiles(getProgramFiles())
+ .addKeepMainRule(getMainClassName())
+ .addDontWarnJetBrainsNotNullAnnotation()
+ .addHorizontallyMergedClassesInspector(this::inspect)
+ .allowAccessModification(allowAccessModification)
+ .allowDiagnosticWarningMessages()
+ .setMinApi(parameters.getApiLevel())
+ .compile()
+ .assertAllWarningMessagesMatch(
+ containsString("Resource 'META-INF/MANIFEST.MF' already exists."))
+ .run(parameters.getRuntime(), getMainClassName())
+ .assertSuccessWithOutput(getExpectedOutput());
+ }
+
+ private void inspect(HorizontallyMergedClassesInspector inspector) throws IOException {
+ // Get the Kotlin lambdas in the input.
+ KotlinLambdasInInput lambdasInInput =
+ KotlinLambdasInInput.create(getProgramFiles(), getTestName());
+ assertEquals(0, lambdasInInput.getNumberOfJStyleLambdas());
+ assertEquals(28, lambdasInInput.getNumberOfKStyleLambdas());
+
+ if (!allowAccessModification) {
+ // Only a subset of all K-style Kotlin lambdas are merged without -allowaccessmodification.
+ Set<ClassReference> unmergedLambdas =
+ ImmutableSet.of(
+ lambdasInInput.getKStyleLambdaReferenceFromTypeName(
+ getTestName(), "inner.InnerKt$testInnerStateless$7"));
+ inspector
+ .assertClassReferencesMerged(
+ lambdasInInput.getKStyleLambdas().stream()
+ .filter(not(unmergedLambdas::contains))
+ .collect(Collectors.toList()))
+ .assertClassReferencesNotMerged(unmergedLambdas);
+ return;
+ }
+
+ // All K-style Kotlin lambdas are merged with -allowaccessmodification.
+ inspector.assertClassReferencesMerged(lambdasInInput.getKStyleLambdas());
+ }
+
+ private String getExpectedOutput() {
+ return StringUtils.lines(
+ "first empty",
+ "second empty",
+ "first single",
+ "second single",
+ "third single",
+ "caught: exception#14",
+ "15",
+ "16-17",
+ "181920",
+ "one-two-three",
+ "one-two-...-twentythree",
+ "46474849505152535455565758596061626364656667",
+ "first empty",
+ "second empty",
+ "first single",
+ "second single",
+ "third single",
+ "71",
+ "72-73",
+ "1",
+ "5",
+ "8",
+ "20",
+ "5",
+ "",
+ "kotlin.Unit",
+ "10",
+ "kotlin.Unit",
+ "13",
+ "kotlin.Unit",
+ "14 -- 10",
+ "kotlin.Unit");
+ }
+
+ private Path getJavaJarFile() {
+ return getJavaJarFile(getTestName());
+ }
+
+ private String getMainClassName() {
+ return getTestName() + ".MainKt";
+ }
+
+ private List<Path> getProgramFiles() {
+ Path kotlinJarFile =
+ getCompileMemoizer(getKotlinFilesInResource(getTestName()), getTestName())
+ .configure(kotlinCompilerTool -> kotlinCompilerTool.includeRuntime().noReflect())
+ .getForConfiguration(kotlinc, targetVersion);
+ return ImmutableList.of(kotlinJarFile, getJavaJarFile());
+ }
+
+ private String getTestName() {
+ return "lambdas_kstyle_trivial";
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/kotlin/lambda/KotlinLambdaMergingWithReprocessingTest.java b/src/test/java/com/android/tools/r8/kotlin/lambda/KotlinLambdaMergingWithReprocessingTest.java
index b8fb366..8258582 100644
--- a/src/test/java/com/android/tools/r8/kotlin/lambda/KotlinLambdaMergingWithReprocessingTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/lambda/KotlinLambdaMergingWithReprocessingTest.java
@@ -3,10 +3,8 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.kotlin.lambda;
-import static com.android.tools.r8.ToolHelper.getKotlinCompilers;
-
-import com.android.tools.r8.KotlinCompilerTool.KotlinCompiler;
-import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
+import com.android.tools.r8.KotlinTestParameters;
+import com.android.tools.r8.TestShrinkerBuilder;
import com.android.tools.r8.kotlin.AbstractR8KotlinTestBase;
import com.android.tools.r8.utils.BooleanUtils;
import java.util.Collection;
@@ -17,15 +15,16 @@
@RunWith(Parameterized.class)
public class KotlinLambdaMergingWithReprocessingTest extends AbstractR8KotlinTestBase {
- @Parameterized.Parameters(name = "target: {0}, kotlinc: {1}, allowAccessModification: {2}")
+ @Parameterized.Parameters(name = "{0}, allowAccessModification: {1}")
public static Collection<Object[]> data() {
return buildParameters(
- KotlinTargetVersion.values(), getKotlinCompilers(), BooleanUtils.values());
+ getKotlinTestParameters().withAllCompilersAndTargetVersions().build(),
+ BooleanUtils.values());
}
public KotlinLambdaMergingWithReprocessingTest(
- KotlinTargetVersion targetVersion, KotlinCompiler kotlinc, boolean allowAccessModification) {
- super(targetVersion, kotlinc, allowAccessModification);
+ KotlinTestParameters kotlinParameters, boolean allowAccessModification) {
+ super(kotlinParameters, allowAccessModification);
}
@Test
@@ -34,14 +33,6 @@
runTest(
"reprocess_merged_lambdas_kstyle",
mainClassName,
- testBuilder ->
- testBuilder
- .addDontWarnJetBrainsNotNullAnnotation()
- .addOptionsModification(
- options -> {
- options.enableInlining = true;
- options.enableClassInlining = true;
- options.enableLambdaMerging = true;
- }));
+ TestShrinkerBuilder::addDontWarnJetBrainsNotNullAnnotation);
}
}
diff --git a/src/test/java/com/android/tools/r8/kotlin/lambda/KotlinLambdaMergingWithSmallInliningBudgetTest.java b/src/test/java/com/android/tools/r8/kotlin/lambda/KotlinLambdaMergingWithSmallInliningBudgetTest.java
index 5032ea2..087a81b 100644
--- a/src/test/java/com/android/tools/r8/kotlin/lambda/KotlinLambdaMergingWithSmallInliningBudgetTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/lambda/KotlinLambdaMergingWithSmallInliningBudgetTest.java
@@ -3,10 +3,7 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.kotlin.lambda;
-import static com.android.tools.r8.ToolHelper.getKotlinCompilers;
-
-import com.android.tools.r8.KotlinCompilerTool.KotlinCompiler;
-import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
+import com.android.tools.r8.KotlinTestParameters;
import com.android.tools.r8.kotlin.AbstractR8KotlinTestBase;
import com.android.tools.r8.utils.BooleanUtils;
import java.util.Collection;
@@ -17,15 +14,16 @@
@RunWith(Parameterized.class)
public class KotlinLambdaMergingWithSmallInliningBudgetTest extends AbstractR8KotlinTestBase {
- @Parameterized.Parameters(name = "target: {0}, kotlinc: {1}, allowAccessModification: {2}")
+ @Parameterized.Parameters(name = "{0}, allowAccessModification: {1}")
public static Collection<Object[]> data() {
return buildParameters(
- KotlinTargetVersion.values(), getKotlinCompilers(), BooleanUtils.values());
+ getKotlinTestParameters().withAllCompilersAndTargetVersions().build(),
+ BooleanUtils.values());
}
public KotlinLambdaMergingWithSmallInliningBudgetTest(
- KotlinTargetVersion targetVersion, KotlinCompiler kotlinc, boolean allowAccessModification) {
- super(targetVersion, kotlinc, allowAccessModification);
+ KotlinTestParameters kotlinParameters, boolean allowAccessModification) {
+ super(kotlinParameters, allowAccessModification);
}
@Test
diff --git a/src/test/java/com/android/tools/r8/kotlin/lambda/KotlinLambdasInInput.java b/src/test/java/com/android/tools/r8/kotlin/lambda/KotlinLambdasInInput.java
new file mode 100644
index 0000000..3517ff3
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/kotlin/lambda/KotlinLambdasInInput.java
@@ -0,0 +1,125 @@
+// Copyright (c) 2021, 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.kotlin.lambda;
+
+import static junit.framework.TestCase.assertTrue;
+
+import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.references.ClassReference;
+import com.android.tools.r8.references.Reference;
+import com.android.tools.r8.utils.SetUtils;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import com.android.tools.r8.utils.codeinspector.FoundClassSubject;
+import java.io.IOException;
+import java.nio.file.Path;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+public class KotlinLambdasInInput {
+
+ private final Set<ClassReference> jStyleLambdas;
+ private final Set<ClassReference> kStyleLambdas;
+
+ private KotlinLambdasInInput(
+ Set<ClassReference> jStyleLambdas, Set<ClassReference> kStyleLambdas) {
+ this.jStyleLambdas = jStyleLambdas;
+ this.kStyleLambdas = kStyleLambdas;
+ }
+
+ public static KotlinLambdasInInput create(List<Path> programFiles, String testName)
+ throws IOException {
+ CodeInspector inputInspector = new CodeInspector(programFiles);
+ Set<ClassReference> jStyleLambdas = new HashSet<>();
+ Set<ClassReference> kStyleLambdas = new HashSet<>();
+ for (FoundClassSubject classSubject : inputInspector.allClasses()) {
+ DexProgramClass clazz = classSubject.getDexProgramClass();
+ if (!clazz.getType().getPackageName().startsWith(testName)) {
+ continue;
+ }
+ if (internalIsJStyleLambda(clazz)) {
+ jStyleLambdas.add(Reference.classFromTypeName(clazz.getTypeName()));
+ } else if (internalIsKStyleLambda(clazz)) {
+ kStyleLambdas.add(Reference.classFromTypeName(clazz.getTypeName()));
+ }
+ }
+ return new KotlinLambdasInInput(jStyleLambdas, kStyleLambdas);
+ }
+
+ private static boolean internalIsKStyleLambda(DexProgramClass clazz) {
+ return clazz.getSuperType().getTypeName().equals("kotlin.jvm.internal.Lambda");
+ }
+
+ private static boolean internalIsJStyleLambda(DexProgramClass clazz) {
+ if (!clazz.getSuperType().getTypeName().equals(Object.class.getTypeName())
+ || clazz.getInterfaces().size() != 1
+ || clazz.getMethodCollection().numberOfVirtualMethods() == 0) {
+ return false;
+ }
+ if (clazz
+ .getMethodCollection()
+ .hasDirectMethods(method -> method.isStatic() && !method.isClassInitializer())) {
+ return false;
+ }
+ int numberOfFinalNonBridgeNonSyntheticMethods = 0;
+ for (DexEncodedMethod method : clazz.virtualMethods()) {
+ if (method.isFinal() && !method.isBridge() && !method.isSyntheticMethod()) {
+ numberOfFinalNonBridgeNonSyntheticMethods++;
+ }
+ }
+ return numberOfFinalNonBridgeNonSyntheticMethods == 1;
+ }
+
+ public Set<ClassReference> getAllLambdas() {
+ return SetUtils.newIdentityHashSet(jStyleLambdas, kStyleLambdas);
+ }
+
+ public Set<ClassReference> getJStyleLambdas() {
+ return jStyleLambdas;
+ }
+
+ public ClassReference getJStyleLambdaReferenceFromTypeName(String testName, String simpleName) {
+ ClassReference classReference = Reference.classFromTypeName(testName + "." + simpleName);
+ assertTrue(jStyleLambdas.contains(classReference));
+ return classReference;
+ }
+
+ public Set<ClassReference> getKStyleLambdas() {
+ return kStyleLambdas;
+ }
+
+ public ClassReference getKStyleLambdaReferenceFromTypeName(String testName, String simpleName) {
+ ClassReference classReference = Reference.classFromTypeName(testName + "." + simpleName);
+ assertTrue(
+ "Class is not a Kotlin-style lambda: " + classReference.getTypeName(),
+ kStyleLambdas.contains(classReference));
+ return classReference;
+ }
+
+ public int getNumberOfJStyleLambdas() {
+ return jStyleLambdas.size();
+ }
+
+ public int getNumberOfKStyleLambdas() {
+ return kStyleLambdas.size();
+ }
+
+ public boolean isJStyleLambda(ClassReference classReference) {
+ return jStyleLambdas.contains(classReference);
+ }
+
+ public boolean isKStyleLambda(ClassReference classReference) {
+ return kStyleLambdas.contains(classReference);
+ }
+
+ public void print() {
+ System.out.println("Java-style Kotlin lambdas:");
+ jStyleLambdas.forEach(lambda -> System.out.println(lambda.getTypeName()));
+ System.out.println();
+ System.out.println("Kotlin-style Kotlin lambdas:");
+ kStyleLambdas.forEach(lambda -> System.out.println(lambda.getTypeName()));
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/kotlin/lambda/KotlinxMetadataExtensionsServiceTest.java b/src/test/java/com/android/tools/r8/kotlin/lambda/KotlinxMetadataExtensionsServiceTest.java
deleted file mode 100644
index 26cc829..0000000
--- a/src/test/java/com/android/tools/r8/kotlin/lambda/KotlinxMetadataExtensionsServiceTest.java
+++ /dev/null
@@ -1,107 +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.kotlin.lambda;
-
-import static com.android.tools.r8.ToolHelper.EXAMPLES_KOTLIN_RESOURCE_DIR;
-import static com.android.tools.r8.kotlin.lambda.KotlinLambdaMergingTest.kstyle;
-import static org.hamcrest.CoreMatchers.containsString;
-import static org.hamcrest.CoreMatchers.not;
-import static org.hamcrest.MatcherAssert.assertThat;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
-
-import com.android.tools.r8.TestBase;
-import com.android.tools.r8.ToolHelper;
-import com.android.tools.r8.ToolHelper.ProcessResult;
-import com.android.tools.r8.kotlin.lambda.KotlinLambdaMergingTest.Group;
-import com.android.tools.r8.kotlin.lambda.KotlinLambdaMergingTest.Lambda;
-import com.android.tools.r8.kotlin.lambda.KotlinLambdaMergingTest.Verifier;
-import com.android.tools.r8.utils.AndroidApiLevel;
-import com.android.tools.r8.utils.FileUtils;
-import com.android.tools.r8.utils.codeinspector.CodeInspector;
-import com.google.common.collect.ImmutableList;
-import java.nio.file.Files;
-import java.nio.file.Path;
-import java.nio.file.Paths;
-import java.util.List;
-import org.junit.Test;
-
-public class KotlinxMetadataExtensionsServiceTest extends TestBase {
-
- private void forkR8_kstyle_trivial(boolean allowAccessModification) throws Exception {
- if (!isRunR8Jar()) {
- return;
- }
- Path working = temp.getRoot().toPath();
- Path kotlinJar =
- Paths.get(EXAMPLES_KOTLIN_RESOURCE_DIR, "JAVA_8", "lambdas_kstyle_trivial.jar")
- .toAbsolutePath();
- Path output = working.resolve("classes.dex");
- assertFalse(Files.exists(output));
- Path proguardConfiguration = temp.newFile("test.conf").toPath();
- List<String> lines = ImmutableList.of(
- "-keepattributes Signature,InnerClasses,EnclosingMethod",
- "-keep class **MainKt {",
- " public static void main(...);",
- "}",
- "-printmapping",
- "-dontobfuscate",
- allowAccessModification ? "-allowaccessmodification" : ""
- );
- FileUtils.writeTextFile(proguardConfiguration, lines);
- ProcessResult result = ToolHelper.forkR8Jar(working,
- "--pg-conf", proguardConfiguration.toString(),
- "--lib", ToolHelper.getAndroidJar(AndroidApiLevel.O).toAbsolutePath().toString(),
- kotlinJar.toString());
- assertEquals(0, result.exitCode);
- assertThat(result.stderr, not(containsString(
- "No MetadataExtensions instances found in the classpath")));
- assertTrue(Files.exists(output));
-
- CodeInspector inspector = new CodeInspector(output);
- Verifier verifier = new Verifier(inspector);
- String pkg = "lambdas_kstyle_trivial";
- verifier.assertLambdaGroups(
- allowAccessModification
- ? new Group[] {
- kstyle("", 0, 4),
- kstyle("", 1, 9),
- kstyle("", 2, 2), // -\
- kstyle("", 2, 5), // - 3 groups different by main method
- kstyle("", 2, 4), // -/
- kstyle("", 3, 2),
- kstyle("", 22, 2)
- }
- : new Group[] {
- kstyle(pkg, 0, 2),
- kstyle(pkg, 1, 5),
- kstyle(pkg, 2, 5), // - 2 groups different by main method
- kstyle(pkg, 2, 4), // -/
- kstyle(pkg, 3, 2),
- kstyle(pkg, 22, 2),
- kstyle(pkg + "/inner", 0, 2),
- kstyle(pkg + "/inner", 1, 4)
- });
-
- verifier.assertLambdas(
- allowAccessModification
- ? new Lambda[] {}
- : new Lambda[] {
- new Lambda(pkg, "MainKt$testStateless$8", 2),
- new Lambda(pkg + "/inner", "InnerKt$testInnerStateless$7", 2)
- });
- }
-
- @Test
- public void testTrivialKs_allowAccessModification() throws Exception {
- forkR8_kstyle_trivial(true);
- }
-
- @Test
- public void testTrivialKs_notAllowAccessModification() throws Exception {
- forkR8_kstyle_trivial(false);
- }
-
-}
diff --git a/src/test/java/com/android/tools/r8/kotlin/lambda/b148525512/B148525512.java b/src/test/java/com/android/tools/r8/kotlin/lambda/b148525512/B148525512.java
index e9f4e46..ac22198 100644
--- a/src/test/java/com/android/tools/r8/kotlin/lambda/b148525512/B148525512.java
+++ b/src/test/java/com/android/tools/r8/kotlin/lambda/b148525512/B148525512.java
@@ -4,31 +4,19 @@
package com.android.tools.r8.kotlin.lambda.b148525512;
-import static com.android.tools.r8.ToolHelper.getKotlinCompilers;
-import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
-import static junit.framework.TestCase.assertEquals;
-import static junit.framework.TestCase.assertTrue;
import static org.hamcrest.CoreMatchers.equalTo;
-import static org.hamcrest.MatcherAssert.assertThat;
import com.android.tools.r8.DexIndexedConsumer.ArchiveConsumer;
-import com.android.tools.r8.KotlinCompilerTool.KotlinCompiler;
import com.android.tools.r8.KotlinTestBase;
+import com.android.tools.r8.KotlinTestParameters;
import com.android.tools.r8.R8TestCompileResult;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.ToolHelper;
-import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
import com.android.tools.r8.utils.ArchiveResourceProvider;
-import com.android.tools.r8.utils.codeinspector.CodeInspector;
-import com.android.tools.r8.utils.codeinspector.FoundClassSubject;
-import com.android.tools.r8.utils.codeinspector.InstructionSubject;
-import com.android.tools.r8.utils.codeinspector.MethodSubject;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Collection;
-import java.util.List;
-import java.util.stream.Collectors;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
@@ -57,17 +45,15 @@
});
private final TestParameters parameters;
- @Parameterized.Parameters(name = "{0}, target: {1}, kotlinc: {2}")
+ @Parameterized.Parameters(name = "{0}, {1}")
public static Collection<Object[]> data() {
return buildParameters(
getTestParameters().withDexRuntimes().withAllApiLevels().build(),
- KotlinTargetVersion.values(),
- getKotlinCompilers());
+ getKotlinTestParameters().withAllCompilersAndTargetVersions().build());
}
- public B148525512(
- TestParameters parameters, KotlinTargetVersion targetVersion, KotlinCompiler kotlinc) {
- super(targetVersion, kotlinc);
+ public B148525512(TestParameters parameters, KotlinTestParameters kotlinParameters) {
+ super(kotlinParameters);
this.parameters = parameters;
}
@@ -84,35 +70,6 @@
}
}
- private void checkLambdaGroups(CodeInspector inspector) {
- List<FoundClassSubject> lambdaGroups =
- inspector.allClasses().stream()
- .filter(clazz -> clazz.getOriginalName().contains("LambdaGroup"))
- .collect(Collectors.toList());
- assertEquals(1, lambdaGroups.size());
- MethodSubject invokeMethod = lambdaGroups.get(0).uniqueMethodWithName("invoke");
- assertThat(invokeMethod, isPresent());
- // The lambda group has 2 captures which capture "Base".
- assertEquals(
- 2,
- invokeMethod
- .streamInstructions()
- .filter(InstructionSubject::isCheckCast)
- .filter(
- instruction ->
- instruction.asCheckCast().getType().toSourceString().contains("Base"))
- .count());
- // The lambda group has no captures which capture "Feature" (lambdas in the feature are not
- // in this lambda group).
- assertTrue(
- invokeMethod
- .streamInstructions()
- .filter(InstructionSubject::isCheckCast)
- .noneMatch(
- instruction ->
- instruction.asCheckCast().getType().toSourceString().contains("Feature")));
- }
-
@Test
public void test() throws Exception {
Path featureCode = temp.newFile("feature.zip").toPath();
@@ -125,10 +82,16 @@
.addKeepClassAndMembersRules(baseClassName)
.addKeepClassAndMembersRules(featureKtClassNamet)
.addKeepClassAndMembersRules(FeatureAPI.class)
- .addOptionsModification(
- options -> options.horizontalClassMergerOptions().disableKotlinLambdaMerging())
+ .addHorizontallyMergedClassesInspector(
+ inspector ->
+ inspector
+ .assertIsCompleteMergeGroup(
+ "com.android.tools.r8.kotlin.lambda.b148525512.BaseKt$main$1",
+ "com.android.tools.r8.kotlin.lambda.b148525512.BaseKt$main$2")
+ .assertIsCompleteMergeGroup(
+ "com.android.tools.r8.kotlin.lambda.b148525512.FeatureKt$feature$1",
+ "com.android.tools.r8.kotlin.lambda.b148525512.FeatureKt$feature$2"))
.setMinApi(parameters.getApiLevel())
- .noMinification() // The check cast inspection above relies on original names.
.addFeatureSplit(
builder ->
builder
@@ -142,10 +105,9 @@
.addDontWarnJetBrainsNotNullAnnotation()
.compile()
.assertAllWarningMessagesMatch(
- equalTo("Resource 'META-INF/MANIFEST.MF' already exists."))
- .inspect(this::checkLambdaGroups);
+ equalTo("Resource 'META-INF/MANIFEST.MF' already exists."));
- // Run the code without the feature code present.
+ // Run the code without the feature code.
compileResult
.run(parameters.getRuntime(), baseKtClassName)
.assertSuccessWithOutputLines("1", "2");
diff --git a/src/test/java/com/android/tools/r8/kotlin/lambda/b159688129/LambdaGroupGCLimitTest.java b/src/test/java/com/android/tools/r8/kotlin/lambda/b159688129/LambdaGroupGCLimitTest.java
index c33d03b..8d476be 100644
--- a/src/test/java/com/android/tools/r8/kotlin/lambda/b159688129/LambdaGroupGCLimitTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/lambda/b159688129/LambdaGroupGCLimitTest.java
@@ -11,21 +11,19 @@
import static org.junit.Assert.assertTrue;
import com.android.tools.r8.CompilationFailedException;
-import com.android.tools.r8.R8FullTestBuilder;
import com.android.tools.r8.R8TestCompileResult;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
import com.android.tools.r8.ToolHelper;
import com.android.tools.r8.ToolHelper.ProcessResult;
-import com.android.tools.r8.utils.BooleanUtils;
import com.android.tools.r8.utils.InternalOptions.HorizontalClassMergerOptions;
-import com.android.tools.r8.utils.codeinspector.FoundClassSubject;
+import com.google.common.collect.ImmutableList;
import java.io.IOException;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutionException;
-import java.util.stream.Collectors;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
@@ -34,36 +32,61 @@
@RunWith(Parameterized.class)
public class LambdaGroupGCLimitTest extends TestBase {
- private final boolean enableHorizontalClassMergingOfKotlinLambdas;
- private final TestParameters parameters;
- private final int LAMBDA_HOLDER_LIMIT = 50;
- private final int LAMBDAS_PER_CLASS_LIMIT = 100;
+ private static final int LAMBDA_HOLDER_LIMIT = 50;
+ private static final int LAMBDAS_PER_CLASS_LIMIT = 100;
- @Parameters(name = "{1}, horizontal class merging: {0}")
- public static List<Object[]> data() {
- return buildParameters(
- BooleanUtils.values(), getTestParameters().withDexRuntimes().withAllApiLevels().build());
+ private final TestParameters parameters;
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withDexRuntimes().withAllApiLevels().build();
}
- public LambdaGroupGCLimitTest(
- boolean enableHorizontalClassMergingOfKotlinLambdas, TestParameters parameters) {
- this.enableHorizontalClassMergingOfKotlinLambdas = enableHorizontalClassMergingOfKotlinLambdas;
+ public LambdaGroupGCLimitTest(TestParameters parameters) {
this.parameters = parameters;
}
@Test
public void testR8() throws ExecutionException, CompilationFailedException, IOException {
String PKG_NAME = LambdaGroupGCLimitTest.class.getPackage().getName();
- R8FullTestBuilder testBuilder =
+ R8TestCompileResult compileResult =
testForR8(parameters.getBackend())
- .addProgramFiles(ToolHelper.getKotlinStdlibJar(ToolHelper.getKotlinC_1_3_72()))
- .addOptionsModification(
- options ->
- options
- .horizontalClassMergerOptions()
- .enableKotlinLambdaMergingIf(enableHorizontalClassMergingOfKotlinLambdas))
+ .addProgramFiles(getProgramFiles())
.setMinApi(parameters.getApiLevel())
- .noMinification();
+ .apply(
+ builder -> {
+ for (int mainId = 0; mainId < LAMBDA_HOLDER_LIMIT; mainId++) {
+ builder.addKeepClassAndMembersRules(PKG_NAME + ".MainKt" + mainId);
+ }
+ })
+ .addDontWarnJetBrainsNotNullAnnotation()
+ .addHorizontallyMergedClassesInspector(
+ inspector -> {
+ HorizontalClassMergerOptions defaultHorizontalClassMergerOptions =
+ new HorizontalClassMergerOptions();
+ assertEquals(4833, inspector.getSources().size());
+ assertEquals(167, inspector.getTargets().size());
+ assertTrue(
+ inspector.getMergeGroups().stream()
+ .allMatch(
+ mergeGroup ->
+ mergeGroup.size()
+ <= defaultHorizontalClassMergerOptions.getMaxGroupSize()));
+ })
+ .compile();
+ Path path = compileResult.writeToZip();
+ compileResult
+ .run(parameters.getRuntime(), PKG_NAME + ".MainKt0")
+ .assertSuccessWithOutputLines("3");
+ Path oatFile = temp.newFile("out.oat").toPath();
+ ProcessResult processResult =
+ ToolHelper.runDex2OatRaw(path, oatFile, parameters.getRuntime().asDex().getVm());
+ assertEquals(0, processResult.exitCode);
+ assertThat(
+ processResult.stderr, not(containsString("Method exceeds compiler instruction limit")));
+ }
+
+ private List<Path> getProgramFiles() throws IOException {
Path classFiles = temp.newFile("classes.jar").toPath();
List<byte[]> classFileData = new ArrayList<>();
for (int mainId = 0; mainId < LAMBDA_HOLDER_LIMIT; mainId++) {
@@ -71,50 +94,9 @@
for (int lambdaId = 0; lambdaId < LAMBDAS_PER_CLASS_LIMIT; lambdaId++) {
classFileData.add(MainKt$main$1Dump.dump(mainId, lambdaId));
}
- testBuilder.addKeepClassAndMembersRules(PKG_NAME + ".MainKt" + mainId);
}
writeClassFileDataToJar(classFiles, classFileData);
- R8TestCompileResult compileResult =
- testBuilder
- .addProgramFiles(classFiles)
- .addHorizontallyMergedClassesInspector(
- inspector -> {
- if (enableHorizontalClassMergingOfKotlinLambdas) {
- HorizontalClassMergerOptions defaultHorizontalClassMergerOptions =
- new HorizontalClassMergerOptions();
- assertEquals(4833, inspector.getSources().size());
- assertEquals(167, inspector.getTargets().size());
- assertTrue(
- inspector.getMergeGroups().stream()
- .allMatch(
- mergeGroup ->
- mergeGroup.size()
- <= defaultHorizontalClassMergerOptions.getMaxGroupSize()));
- } else {
- inspector.assertNoClassesMerged();
- }
- })
- .addDontWarnJetBrainsNotNullAnnotation()
- .compile();
- Path path = compileResult.writeToZip();
- compileResult
- .run(parameters.getRuntime(), PKG_NAME + ".MainKt0")
- .assertSuccessWithOutputLines("3")
- .inspect(
- codeInspector -> {
- List<FoundClassSubject> lambdaGroups =
- codeInspector.allClasses().stream()
- .filter(c -> c.getFinalName().contains("LambdaGroup"))
- .collect(Collectors.toList());
- assertEquals(
- 1 - BooleanUtils.intValue(enableHorizontalClassMergingOfKotlinLambdas),
- lambdaGroups.size());
- });
- Path oatFile = temp.newFile("out.oat").toPath();
- ProcessResult processResult =
- ToolHelper.runDex2OatRaw(path, oatFile, parameters.getRuntime().asDex().getVm());
- assertEquals(0, processResult.exitCode);
- assertThat(
- processResult.stderr, not(containsString("Method exceeds compiler instruction limit")));
+ return ImmutableList.of(
+ classFiles, ToolHelper.getKotlinStdlibJar(ToolHelper.getKotlinC_1_3_72()));
}
}
diff --git a/src/test/java/com/android/tools/r8/kotlin/lambda/b159688129/LambdaSplitByCodeCorrectnessTest.java b/src/test/java/com/android/tools/r8/kotlin/lambda/b159688129/LambdaSplitByCodeCorrectnessTest.java
index 663c081..79cef64 100644
--- a/src/test/java/com/android/tools/r8/kotlin/lambda/b159688129/LambdaSplitByCodeCorrectnessTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/lambda/b159688129/LambdaSplitByCodeCorrectnessTest.java
@@ -4,24 +4,25 @@
package com.android.tools.r8.kotlin.lambda.b159688129;
-import static com.android.tools.r8.ToolHelper.getKotlinCompilers;
+import static com.android.tools.r8.utils.codeinspector.CodeMatchers.invokesMethod;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
import static org.hamcrest.CoreMatchers.equalTo;
+import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.Assert.assertEquals;
-import com.android.tools.r8.KotlinCompilerTool.KotlinCompiler;
+import com.android.tools.r8.KotlinTestParameters;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestRuntime;
import com.android.tools.r8.TestRuntime.CfRuntime;
import com.android.tools.r8.ToolHelper;
-import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
import com.android.tools.r8.kotlin.AbstractR8KotlinTestBase;
import com.android.tools.r8.utils.BooleanUtils;
import com.android.tools.r8.utils.DescriptorUtils;
-import com.android.tools.r8.utils.codeinspector.FoundClassSubject;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
import com.android.tools.r8.utils.codeinspector.FoundMethodSubject;
+import com.android.tools.r8.utils.codeinspector.MethodSubject;
import java.nio.file.Path;
import java.util.List;
-import java.util.stream.Collectors;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
@@ -31,26 +32,20 @@
public class LambdaSplitByCodeCorrectnessTest extends AbstractR8KotlinTestBase {
private final TestParameters parameters;
- private final KotlinTargetVersion targetVersion;
private final boolean splitGroup;
- @Parameters(name = "{0}, kotlinc: {2} targetVersion: {1}, splitGroup: {3}")
+ @Parameters(name = "{0}, {1}, splitGroup: {2}")
public static List<Object[]> data() {
return buildParameters(
getTestParameters().withDexRuntimes().withAllApiLevels().build(),
- KotlinTargetVersion.values(),
- getKotlinCompilers(),
+ getKotlinTestParameters().withAllCompilersAndTargetVersions().build(),
BooleanUtils.values());
}
public LambdaSplitByCodeCorrectnessTest(
- TestParameters parameters,
- KotlinTargetVersion targetVersion,
- KotlinCompiler kotlinc,
- boolean splitGroup) {
- super(targetVersion, kotlinc);
+ TestParameters parameters, KotlinTestParameters kotlinParameters, boolean splitGroup) {
+ super(kotlinParameters);
this.parameters = parameters;
- this.targetVersion = targetVersion;
this.splitGroup = splitGroup;
}
@@ -67,8 +62,6 @@
testForR8(parameters.getBackend())
.addProgramFiles(ToolHelper.getKotlinStdlibJar(kotlinc))
.addProgramFiles(ktClasses)
- .addOptionsModification(
- options -> options.horizontalClassMergerOptions().disableKotlinLambdaMerging())
.setMinApi(parameters.getApiLevel())
.addKeepMainRule(PKG_NAME + ".SimpleKt")
.addDontWarnJetBrainsNotNullAnnotation()
@@ -77,25 +70,40 @@
b ->
b.addOptionsModification(
internalOptions ->
- // Setting verificationSizeLimitInBytesOverride = 1 will force a a chain
- // having only a single implementation method in each.
- internalOptions.testing.verificationSizeLimitInBytesOverride =
- splitGroup ? 1 : -1))
- .noMinification()
+ // Setting inliningInstructionAllowance = 1 will force each switch branch to
+ // contain an invoke instruction to a private method.
+ internalOptions.inliningInstructionAllowance = 1))
+ .addHorizontallyMergedClassesInspector(
+ inspector ->
+ inspector.assertIsCompleteMergeGroup(
+ "com.android.tools.r8.kotlin.lambda.b159688129.SimpleKt$main$1",
+ "com.android.tools.r8.kotlin.lambda.b159688129.SimpleKt$main$2",
+ "com.android.tools.r8.kotlin.lambda.b159688129.SimpleKt$main$3",
+ "com.android.tools.r8.kotlin.lambda.b159688129.SimpleKt$main$4",
+ "com.android.tools.r8.kotlin.lambda.b159688129.SimpleKt$main$5",
+ "com.android.tools.r8.kotlin.lambda.b159688129.SimpleKt$main$6"))
.allowDiagnosticWarningMessages()
.compile()
.assertAllWarningMessagesMatch(equalTo("Resource 'META-INF/MANIFEST.MF' already exists."))
.inspect(
codeInspector -> {
- List<FoundClassSubject> lambdaGroups =
- codeInspector.allClasses().stream()
- .filter(c -> c.getFinalName().contains("LambdaGroup"))
- .collect(Collectors.toList());
- assertEquals(1, lambdaGroups.size());
- FoundClassSubject lambdaGroup = lambdaGroups.get(0);
- List<FoundMethodSubject> invokeChain =
- lambdaGroup.allMethods(method -> method.getFinalName().contains("invoke$"));
- assertEquals(splitGroup ? 5 : 0, invokeChain.size());
+ ClassSubject mergeTarget =
+ codeInspector.clazz(
+ "com.android.tools.r8.kotlin.lambda.b159688129.SimpleKt$main$1");
+ assertThat(mergeTarget, isPresent());
+
+ MethodSubject virtualMethodSubject =
+ mergeTarget.uniqueMethodThatMatches(
+ method -> method.isVirtual() && !method.isSynthetic());
+ assertThat(virtualMethodSubject, isPresent());
+
+ int found = 0;
+ for (FoundMethodSubject directMethodSubject :
+ mergeTarget.allMethods(x -> x.isPrivate() && !x.isSynthetic())) {
+ assertThat(virtualMethodSubject, invokesMethod(directMethodSubject));
+ found++;
+ }
+ assertEquals(splitGroup ? 6 : 0, found);
})
.run(parameters.getRuntime(), PKG_NAME + ".SimpleKt")
.assertSuccessWithOutputLines("Hello1", "Hello2", "Hello3", "Hello4", "Hello5", "Hello6");
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/KotlinMetadataTestBase.java b/src/test/java/com/android/tools/r8/kotlin/metadata/KotlinMetadataTestBase.java
index e158fc7..3765187 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/KotlinMetadataTestBase.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/KotlinMetadataTestBase.java
@@ -8,8 +8,7 @@
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.Assert.assertNull;
-import com.android.tools.r8.KotlinCompilerTool.KotlinCompiler;
-import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
+import com.android.tools.r8.KotlinTestParameters;
import com.android.tools.r8.kotlin.AbstractR8KotlinTestBase;
import com.android.tools.r8.kotlin.KotlinMetadataWriter;
import com.android.tools.r8.utils.DescriptorUtils;
@@ -22,8 +21,8 @@
public abstract class KotlinMetadataTestBase extends AbstractR8KotlinTestBase {
- public KotlinMetadataTestBase(KotlinTargetVersion targetVersion, KotlinCompiler kotlinc) {
- super(targetVersion, kotlinc);
+ public KotlinMetadataTestBase(KotlinTestParameters kotlinParameters) {
+ super(kotlinParameters);
}
static final String PKG = KotlinMetadataTestBase.class.getPackage().getName();
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataPrimitiveTypeRewriteTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataPrimitiveTypeRewriteTest.java
index b4fd614..b0a30d7 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataPrimitiveTypeRewriteTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataPrimitiveTypeRewriteTest.java
@@ -4,17 +4,15 @@
package com.android.tools.r8.kotlin.metadata;
-import static com.android.tools.r8.ToolHelper.getKotlinCompilers;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresentAndRenamed;
import static org.hamcrest.CoreMatchers.containsString;
import static org.hamcrest.CoreMatchers.equalTo;
import static org.hamcrest.MatcherAssert.assertThat;
import com.android.tools.r8.JvmTestRunResult;
-import com.android.tools.r8.KotlinCompilerTool.KotlinCompiler;
+import com.android.tools.r8.KotlinTestParameters;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.ToolHelper;
-import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
import com.android.tools.r8.shaking.ProguardKeepAttributes;
import com.android.tools.r8.utils.DescriptorUtils;
import com.android.tools.r8.utils.codeinspector.ClassSubject;
@@ -32,17 +30,16 @@
private static final String PKG_LIB = PKG + ".primitive_type_rewrite_lib";
private static final String PKG_APP = PKG + ".primitive_type_rewrite_app";
- @Parameterized.Parameters(name = "{0}, target: {1}, compiler: {2}")
+ @Parameterized.Parameters(name = "{0}, {1}")
public static Collection<Object[]> data() {
return buildParameters(
getTestParameters().withCfRuntimes().build(),
- KotlinTargetVersion.values(),
- getKotlinCompilers());
+ getKotlinTestParameters().withAllCompilersAndTargetVersions().build());
}
public MetadataPrimitiveTypeRewriteTest(
- TestParameters parameters, KotlinTargetVersion targetVersion, KotlinCompiler kotlinc) {
- super(targetVersion, kotlinc);
+ TestParameters parameters, KotlinTestParameters kotlinParameters) {
+ super(kotlinParameters);
this.parameters = parameters;
}
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataPrunedFieldsTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataPrunedFieldsTest.java
index a048351..09c2a9d 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataPrunedFieldsTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataPrunedFieldsTest.java
@@ -4,16 +4,14 @@
package com.android.tools.r8.kotlin.metadata;
-import static com.android.tools.r8.ToolHelper.getKotlinCompilers;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
import static org.hamcrest.CoreMatchers.equalTo;
import static org.hamcrest.CoreMatchers.not;
import static org.hamcrest.MatcherAssert.assertThat;
-import com.android.tools.r8.KotlinCompilerTool.KotlinCompiler;
+import com.android.tools.r8.KotlinTestParameters;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.ToolHelper;
-import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
import com.android.tools.r8.kotlin.metadata.metadata_pruned_fields.Main;
import com.android.tools.r8.shaking.ProguardKeepAttributes;
import com.android.tools.r8.utils.codeinspector.ClassSubject;
@@ -28,17 +26,16 @@
private final TestParameters parameters;
- @Parameterized.Parameters(name = "{0}, target: {1}, kotlinc: {2}")
+ @Parameterized.Parameters(name = "{0}, {1}")
public static Collection<Object[]> data() {
return buildParameters(
getTestParameters().withAllRuntimesAndApiLevels().build(),
- KotlinTargetVersion.values(),
- getKotlinCompilers());
+ getKotlinTestParameters().withAllCompilersAndTargetVersions().build());
}
public MetadataPrunedFieldsTest(
- TestParameters parameters, KotlinTargetVersion targetVersion, KotlinCompiler kotlinc) {
- super(targetVersion, kotlinc);
+ TestParameters parameters, KotlinTestParameters kotlinParameters) {
+ super(kotlinParameters);
this.parameters = parameters;
}
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteAllowAccessModificationTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteAllowAccessModificationTest.java
index b6d7701..71bd275 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteAllowAccessModificationTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteAllowAccessModificationTest.java
@@ -4,16 +4,14 @@
package com.android.tools.r8.kotlin.metadata;
-import static com.android.tools.r8.ToolHelper.getKotlinCompilers;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
import static junit.framework.TestCase.assertEquals;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.core.StringContains.containsString;
-import com.android.tools.r8.KotlinCompilerTool.KotlinCompiler;
+import com.android.tools.r8.KotlinTestParameters;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.ToolHelper;
-import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
import com.android.tools.r8.ToolHelper.ProcessResult;
import com.android.tools.r8.utils.DescriptorUtils;
import com.android.tools.r8.utils.StringUtils;
@@ -52,17 +50,16 @@
"staticPrivate",
"staticInternal");
- @Parameterized.Parameters(name = "{0}, target: {1}, kotlinc: {2}")
+ @Parameterized.Parameters(name = "{0}, {1}")
public static Collection<Object[]> data() {
return buildParameters(
getTestParameters().withCfRuntimes().build(),
- KotlinTargetVersion.values(),
- getKotlinCompilers());
+ getKotlinTestParameters().withAllCompilersAndTargetVersions().build());
}
public MetadataRewriteAllowAccessModificationTest(
- TestParameters parameters, KotlinTargetVersion targetVersion, KotlinCompiler kotlinc) {
- super(targetVersion, kotlinc);
+ TestParameters parameters, KotlinTestParameters kotlinParameters) {
+ super(kotlinParameters);
this.parameters = parameters;
}
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteAnnotationTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteAnnotationTest.java
index a285149..ebe0dac 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteAnnotationTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteAnnotationTest.java
@@ -4,17 +4,15 @@
package com.android.tools.r8.kotlin.metadata;
-import static com.android.tools.r8.ToolHelper.getKotlinCompilers;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresentAndNotRenamed;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresentAndRenamed;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.Assert.assertEquals;
-import com.android.tools.r8.KotlinCompilerTool.KotlinCompiler;
+import com.android.tools.r8.KotlinTestParameters;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.ToolHelper;
-import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
import com.android.tools.r8.shaking.ProguardKeepAttributes;
import com.android.tools.r8.utils.DescriptorUtils;
import com.android.tools.r8.utils.StringUtils;
@@ -58,17 +56,16 @@
private static final String FOO_ORIGINAL_NAME = PKG_LIB + ".Foo";
private static final String FOO_FINAL_NAME = "a.b.c";
- @Parameterized.Parameters(name = "{0}, target: {1}, kotlinc: {2}")
+ @Parameterized.Parameters(name = "{0}, {1}")
public static Collection<Object[]> data() {
return buildParameters(
getTestParameters().withCfRuntimes().build(),
- KotlinTargetVersion.values(),
- getKotlinCompilers());
+ getKotlinTestParameters().withAllCompilersAndTargetVersions().build());
}
public MetadataRewriteAnnotationTest(
- TestParameters parameters, KotlinTargetVersion targetVersion, KotlinCompiler kotlinc) {
- super(targetVersion, kotlinc);
+ TestParameters parameters, KotlinTestParameters kotlinParameters) {
+ super(kotlinParameters);
this.parameters = parameters;
}
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteAnonymousTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteAnonymousTest.java
index 8db1944..374c3fd 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteAnonymousTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteAnonymousTest.java
@@ -4,14 +4,12 @@
package com.android.tools.r8.kotlin.metadata;
-import static com.android.tools.r8.ToolHelper.getKotlinCompilers;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresentAndRenamed;
import static org.hamcrest.MatcherAssert.assertThat;
-import com.android.tools.r8.KotlinCompilerTool.KotlinCompiler;
+import com.android.tools.r8.KotlinTestParameters;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.ToolHelper;
-import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
import com.android.tools.r8.kotlin.KotlinMetadataWriter;
import com.android.tools.r8.shaking.ProguardKeepAttributes;
import com.android.tools.r8.utils.codeinspector.ClassSubject;
@@ -27,17 +25,16 @@
private final String EXPECTED = "foo";
- @Parameterized.Parameters(name = "{0}, target: {1}, kotlinc: {2}")
+ @Parameterized.Parameters(name = "{0}, {1}")
public static Collection<Object[]> data() {
return buildParameters(
getTestParameters().withCfRuntimes().build(),
- KotlinTargetVersion.values(),
- getKotlinCompilers());
+ getKotlinTestParameters().withAllCompilersAndTargetVersions().build());
}
public MetadataRewriteAnonymousTest(
- TestParameters parameters, KotlinTargetVersion targetVersion, KotlinCompiler kotlinc) {
- super(targetVersion, kotlinc);
+ TestParameters parameters, KotlinTestParameters kotlinParameters) {
+ super(kotlinParameters);
this.parameters = parameters;
}
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteBoxedTypesTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteBoxedTypesTest.java
index 4e7d224..16d3765 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteBoxedTypesTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteBoxedTypesTest.java
@@ -4,17 +4,15 @@
package com.android.tools.r8.kotlin.metadata;
-import static com.android.tools.r8.ToolHelper.getKotlinCompilers;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
import static junit.framework.TestCase.assertEquals;
import static junit.framework.TestCase.assertNotNull;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.Assert.assertNull;
-import com.android.tools.r8.KotlinCompilerTool.KotlinCompiler;
+import com.android.tools.r8.KotlinTestParameters;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.ToolHelper;
-import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
import com.android.tools.r8.kotlin.KotlinMetadataWriter;
import com.android.tools.r8.shaking.ProguardKeepAttributes;
import com.android.tools.r8.utils.StringUtils;
@@ -37,17 +35,16 @@
private final String EXPECTED =
StringUtils.lines("false", "0", "a", "0.042", "0.42", "42", "442", "1", "2", "42", "42");
- @Parameterized.Parameters(name = "{0}, target: {1}, kotlinc: {2}")
+ @Parameterized.Parameters(name = "{0}, {1}")
public static Collection<Object[]> data() {
return buildParameters(
getTestParameters().withCfRuntimes().build(),
- KotlinTargetVersion.values(),
- getKotlinCompilers());
+ getKotlinTestParameters().withAllCompilersAndTargetVersions().build());
}
public MetadataRewriteBoxedTypesTest(
- TestParameters parameters, KotlinTargetVersion targetVersion, KotlinCompiler kotlinc) {
- super(targetVersion, kotlinc);
+ TestParameters parameters, KotlinTestParameters kotlinParameters) {
+ super(kotlinParameters);
this.parameters = parameters;
}
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteCrossinlineAnonFunctionTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteCrossinlineAnonFunctionTest.java
index 065d63f..989512b 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteCrossinlineAnonFunctionTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteCrossinlineAnonFunctionTest.java
@@ -4,12 +4,9 @@
package com.android.tools.r8.kotlin.metadata;
-import static com.android.tools.r8.ToolHelper.getKotlinCompilers;
-
-import com.android.tools.r8.KotlinCompilerTool.KotlinCompiler;
+import com.android.tools.r8.KotlinTestParameters;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.ToolHelper;
-import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
import com.android.tools.r8.utils.DescriptorUtils;
import com.android.tools.r8.utils.StringUtils;
import java.nio.file.Path;
@@ -25,17 +22,16 @@
private static final String PKG_LIB = PKG + ".crossinline_anon_lib";
private static final String PKG_APP = PKG + ".crossinline_anon_app";
- @Parameterized.Parameters(name = "{0}, target: {1}, kotlinc: {2}")
+ @Parameterized.Parameters(name = "{0}, {1}")
public static Collection<Object[]> data() {
return buildParameters(
getTestParameters().withCfRuntimes().build(),
- KotlinTargetVersion.values(),
- getKotlinCompilers());
+ getKotlinTestParameters().withAllCompilersAndTargetVersions().build());
}
public MetadataRewriteCrossinlineAnonFunctionTest(
- TestParameters parameters, KotlinTargetVersion targetVersion, KotlinCompiler kotlinc) {
- super(targetVersion, kotlinc);
+ TestParameters parameters, KotlinTestParameters kotlinParameters) {
+ super(kotlinParameters);
this.parameters = parameters;
}
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteCrossinlineConcreteFunctionTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteCrossinlineConcreteFunctionTest.java
index 15f68ea..d969f34 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteCrossinlineConcreteFunctionTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteCrossinlineConcreteFunctionTest.java
@@ -4,12 +4,9 @@
package com.android.tools.r8.kotlin.metadata;
-import static com.android.tools.r8.ToolHelper.getKotlinCompilers;
-
-import com.android.tools.r8.KotlinCompilerTool.KotlinCompiler;
+import com.android.tools.r8.KotlinTestParameters;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.ToolHelper;
-import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
import com.android.tools.r8.utils.DescriptorUtils;
import com.android.tools.r8.utils.StringUtils;
import java.nio.file.Path;
@@ -25,17 +22,16 @@
private static final String PKG_LIB = PKG + ".crossinline_concrete_lib";
private static final String PKG_APP = PKG + ".crossinline_concrete_app";
- @Parameterized.Parameters(name = "{0}, target: {1}, kotlinc: {2}")
+ @Parameterized.Parameters(name = "{0}, {1}")
public static Collection<Object[]> data() {
return buildParameters(
getTestParameters().withCfRuntimes().build(),
- KotlinTargetVersion.values(),
- getKotlinCompilers());
+ getKotlinTestParameters().withAllCompilersAndTargetVersions().build());
}
public MetadataRewriteCrossinlineConcreteFunctionTest(
- TestParameters parameters, KotlinTargetVersion targetVersion, KotlinCompiler kotlinc) {
- super(targetVersion, kotlinc);
+ TestParameters parameters, KotlinTestParameters kotlinParameters) {
+ super(kotlinParameters);
this.parameters = parameters;
}
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 b43f846..bc8051d 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
@@ -4,12 +4,9 @@
package com.android.tools.r8.kotlin.metadata;
-import static com.android.tools.r8.ToolHelper.getKotlinCompilers;
-
-import com.android.tools.r8.KotlinCompilerTool.KotlinCompiler;
+import com.android.tools.r8.KotlinTestParameters;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.ToolHelper;
-import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
import com.android.tools.r8.shaking.ProguardKeepAttributes;
import com.android.tools.r8.utils.DescriptorUtils;
import com.android.tools.r8.utils.StringUtils;
@@ -33,17 +30,16 @@
"null",
"New value has been read in CustomDelegate from 'x'");
- @Parameterized.Parameters(name = "{0}, target: {1}, kotlinc: {2}")
+ @Parameterized.Parameters(name = "{0}, {1}")
public static Collection<Object[]> data() {
return buildParameters(
getTestParameters().withCfRuntimes().build(),
- KotlinTargetVersion.values(),
- getKotlinCompilers());
+ getKotlinTestParameters().withAllCompilersAndTargetVersions().build());
}
public MetadataRewriteDelegatedPropertyTest(
- TestParameters parameters, KotlinTargetVersion targetVersion, KotlinCompiler kotlinc) {
- super(targetVersion, kotlinc);
+ TestParameters parameters, KotlinTestParameters kotlinParameters) {
+ super(kotlinParameters);
this.parameters = parameters;
}
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteDependentKeep.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteDependentKeep.java
index 2ccbb3a..3f42968 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteDependentKeep.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteDependentKeep.java
@@ -4,15 +4,13 @@
package com.android.tools.r8.kotlin.metadata;
-import static com.android.tools.r8.ToolHelper.getKotlinCompilers;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import com.android.tools.r8.CompilationFailedException;
-import com.android.tools.r8.KotlinCompilerTool.KotlinCompiler;
+import com.android.tools.r8.KotlinTestParameters;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.ToolHelper;
-import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
import com.android.tools.r8.shaking.ProguardKeepAttributes;
import com.android.tools.r8.utils.StringUtils;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
@@ -27,19 +25,18 @@
@RunWith(Parameterized.class)
public class MetadataRewriteDependentKeep extends KotlinMetadataTestBase {
- @Parameterized.Parameters(name = "{0}, target: {1}, kotlinc: {2}")
+ @Parameterized.Parameters(name = "{0}, {1}")
public static Collection<Object[]> data() {
return buildParameters(
getTestParameters().withCfRuntimes().build(),
- KotlinTargetVersion.values(),
- getKotlinCompilers());
+ getKotlinTestParameters().withAllCompilersAndTargetVersions().build());
}
private final TestParameters parameters;
public MetadataRewriteDependentKeep(
- TestParameters parameters, KotlinTargetVersion targetVersion, KotlinCompiler kotlinc) {
- super(targetVersion, kotlinc);
+ TestParameters parameters, KotlinTestParameters kotlinParameters) {
+ super(kotlinParameters);
this.parameters = parameters;
}
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteDoNotEmitValuesIfEmpty.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteDoNotEmitValuesIfEmpty.java
index feca0f8..1caec7d 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteDoNotEmitValuesIfEmpty.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteDoNotEmitValuesIfEmpty.java
@@ -4,14 +4,12 @@
package com.android.tools.r8.kotlin.metadata;
-import static com.android.tools.r8.ToolHelper.getKotlinCompilers;
import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertTrue;
-import com.android.tools.r8.KotlinCompilerTool.KotlinCompiler;
+import com.android.tools.r8.KotlinTestParameters;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.ToolHelper;
-import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
import com.android.tools.r8.graph.DexAnnotationElement;
import com.android.tools.r8.shaking.ProguardKeepAttributes;
import com.android.tools.r8.utils.codeinspector.AnnotationSubject;
@@ -29,19 +27,18 @@
private final Set<String> nullableFieldKeys = Sets.newHashSet("pn", "xs", "xi");
- @Parameterized.Parameters(name = "{0}, target: {1}, kotlinc: {2}")
+ @Parameterized.Parameters(name = "{0}, {1}")
public static Collection<Object[]> data() {
return buildParameters(
getTestParameters().withCfRuntimes().build(),
- KotlinTargetVersion.values(),
- getKotlinCompilers());
+ getKotlinTestParameters().withAllCompilersAndTargetVersions().build());
}
private final TestParameters parameters;
public MetadataRewriteDoNotEmitValuesIfEmpty(
- TestParameters parameters, KotlinTargetVersion targetVersion, KotlinCompiler kotlinc) {
- super(targetVersion, kotlinc);
+ TestParameters parameters, KotlinTestParameters kotlinParameters) {
+ super(kotlinParameters);
this.parameters = parameters;
}
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteFlexibleUpperBoundTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteFlexibleUpperBoundTest.java
index fbfc10f..91ede26 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteFlexibleUpperBoundTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteFlexibleUpperBoundTest.java
@@ -4,7 +4,6 @@
package com.android.tools.r8.kotlin.metadata;
-import static com.android.tools.r8.ToolHelper.getKotlinCompilers;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresentAndNotRenamed;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresentAndRenamed;
@@ -12,10 +11,9 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
-import com.android.tools.r8.KotlinCompilerTool.KotlinCompiler;
+import com.android.tools.r8.KotlinTestParameters;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.ToolHelper;
-import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
import com.android.tools.r8.shaking.ProguardKeepAttributes;
import com.android.tools.r8.utils.StringUtils;
import com.android.tools.r8.utils.codeinspector.ClassSubject;
@@ -39,17 +37,16 @@
private final String EXPECTED = StringUtils.lines("B.foo(): 42");
private final String PKG_LIB = PKG + ".flexible_upper_bound_lib";
- @Parameterized.Parameters(name = "{0}, target: {1}, kotlinc: {2}")
+ @Parameterized.Parameters(name = "{0}, {1}")
public static Collection<Object[]> data() {
return buildParameters(
getTestParameters().withCfRuntimes().build(),
- KotlinTargetVersion.values(),
- getKotlinCompilers());
+ getKotlinTestParameters().withAllCompilersAndTargetVersions().build());
}
public MetadataRewriteFlexibleUpperBoundTest(
- TestParameters parameters, KotlinTargetVersion targetVersion, KotlinCompiler kotlinc) {
- super(targetVersion, kotlinc);
+ TestParameters parameters, KotlinTestParameters kotlinParameters) {
+ super(kotlinParameters);
this.parameters = parameters;
}
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInClasspathTypeTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInClasspathTypeTest.java
index c7b9ea6..eebb77f 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInClasspathTypeTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInClasspathTypeTest.java
@@ -3,7 +3,6 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.kotlin.metadata;
-import static com.android.tools.r8.ToolHelper.getKotlinCompilers;
import static com.android.tools.r8.utils.codeinspector.Matchers.isExtensionFunction;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresentAndNotRenamed;
@@ -11,10 +10,9 @@
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.Assert.assertTrue;
-import com.android.tools.r8.KotlinCompilerTool.KotlinCompiler;
+import com.android.tools.r8.KotlinTestParameters;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.ToolHelper;
-import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
import com.android.tools.r8.shaking.ProguardKeepAttributes;
import com.android.tools.r8.utils.StringUtils;
import com.android.tools.r8.utils.codeinspector.ClassSubject;
@@ -35,17 +33,16 @@
private final TestParameters parameters;
- @Parameterized.Parameters(name = "{0}, target: {1}, kotlinc: {2}")
+ @Parameterized.Parameters(name = "{0}, {1}")
public static Collection<Object[]> data() {
return buildParameters(
getTestParameters().withCfRuntimes().build(),
- KotlinTargetVersion.values(),
- getKotlinCompilers());
+ getKotlinTestParameters().withAllCompilersAndTargetVersions().build());
}
public MetadataRewriteInClasspathTypeTest(
- TestParameters parameters, KotlinTargetVersion targetVersion, KotlinCompiler kotlinc) {
- super(targetVersion, kotlinc);
+ TestParameters parameters, KotlinTestParameters kotlinParameters) {
+ super(kotlinParameters);
this.parameters = parameters;
}
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInCompanionTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInCompanionTest.java
index bdcb905..9106a3b 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInCompanionTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInCompanionTest.java
@@ -3,7 +3,6 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.kotlin.metadata;
-import static com.android.tools.r8.ToolHelper.getKotlinCompilers;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresentAndNotRenamed;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresentAndRenamed;
@@ -12,10 +11,9 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
-import com.android.tools.r8.KotlinCompilerTool.KotlinCompiler;
+import com.android.tools.r8.KotlinTestParameters;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.ToolHelper;
-import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
import com.android.tools.r8.shaking.ProguardKeepAttributes;
import com.android.tools.r8.utils.StringUtils;
import com.android.tools.r8.utils.codeinspector.ClassSubject;
@@ -44,17 +42,16 @@
private final TestParameters parameters;
- @Parameterized.Parameters(name = "{0}, target: {1}, kotlinc: {2}")
+ @Parameterized.Parameters(name = "{0}, {1}")
public static Collection<Object[]> data() {
return buildParameters(
getTestParameters().withCfRuntimes().build(),
- KotlinTargetVersion.values(),
- getKotlinCompilers());
+ getKotlinTestParameters().withAllCompilersAndTargetVersions().build());
}
public MetadataRewriteInCompanionTest(
- TestParameters parameters, KotlinTargetVersion targetVersion, KotlinCompiler kotlinc) {
- super(targetVersion, kotlinc);
+ TestParameters parameters, KotlinTestParameters kotlinParameters) {
+ super(kotlinParameters);
this.parameters = parameters;
}
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInExtensionFunctionTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInExtensionFunctionTest.java
index f9124bd..f46402b 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInExtensionFunctionTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInExtensionFunctionTest.java
@@ -3,7 +3,6 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.kotlin.metadata;
-import static com.android.tools.r8.ToolHelper.getKotlinCompilers;
import static com.android.tools.r8.utils.codeinspector.Matchers.isExtensionFunction;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresentAndNotRenamed;
@@ -13,10 +12,9 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
-import com.android.tools.r8.KotlinCompilerTool.KotlinCompiler;
+import com.android.tools.r8.KotlinTestParameters;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.ToolHelper;
-import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
import com.android.tools.r8.shaking.ProguardKeepAttributes;
import com.android.tools.r8.utils.StringUtils;
import com.android.tools.r8.utils.codeinspector.ClassSubject;
@@ -41,17 +39,16 @@
private final TestParameters parameters;
- @Parameterized.Parameters(name = "{0}, target: {1}, kotlinc: {2}")
+ @Parameterized.Parameters(name = "{0}, {1}")
public static Collection<Object[]> data() {
return buildParameters(
getTestParameters().withCfRuntimes().build(),
- KotlinTargetVersion.values(),
- getKotlinCompilers());
+ getKotlinTestParameters().withAllCompilersAndTargetVersions().build());
}
public MetadataRewriteInExtensionFunctionTest(
- TestParameters parameters, KotlinTargetVersion targetVersion, KotlinCompiler kotlinc) {
- super(targetVersion, kotlinc);
+ TestParameters parameters, KotlinTestParameters kotlinParameters) {
+ super(kotlinParameters);
this.parameters = parameters;
}
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInExtensionPropertyTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInExtensionPropertyTest.java
index c78cea1..c2a923f 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInExtensionPropertyTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInExtensionPropertyTest.java
@@ -3,7 +3,6 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.kotlin.metadata;
-import static com.android.tools.r8.ToolHelper.getKotlinCompilers;
import static com.android.tools.r8.utils.codeinspector.Matchers.isExtensionFunction;
import static com.android.tools.r8.utils.codeinspector.Matchers.isExtensionProperty;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
@@ -14,10 +13,9 @@
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
-import com.android.tools.r8.KotlinCompilerTool.KotlinCompiler;
+import com.android.tools.r8.KotlinTestParameters;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.ToolHelper;
-import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
import com.android.tools.r8.shaking.ProguardKeepAttributes;
import com.android.tools.r8.utils.StringUtils;
import com.android.tools.r8.utils.codeinspector.ClassSubject;
@@ -40,17 +38,16 @@
private final TestParameters parameters;
- @Parameterized.Parameters(name = "{0}, target: {1}, kotlinc: {2}")
+ @Parameterized.Parameters(name = "{0}, {1}")
public static Collection<Object[]> data() {
return buildParameters(
getTestParameters().withCfRuntimes().build(),
- KotlinTargetVersion.values(),
- getKotlinCompilers());
+ getKotlinTestParameters().withAllCompilersAndTargetVersions().build());
}
public MetadataRewriteInExtensionPropertyTest(
- TestParameters parameters, KotlinTargetVersion targetVersion, KotlinCompiler kotlinc) {
- super(targetVersion, kotlinc);
+ TestParameters parameters, KotlinTestParameters kotlinParameters) {
+ super(kotlinParameters);
this.parameters = parameters;
}
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInFunctionTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInFunctionTest.java
index 5c8468d..48044e2 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInFunctionTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInFunctionTest.java
@@ -3,7 +3,6 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.kotlin.metadata;
-import static com.android.tools.r8.ToolHelper.getKotlinCompilers;
import static com.android.tools.r8.utils.codeinspector.Matchers.isExtensionFunction;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresentAndNotRenamed;
@@ -13,10 +12,9 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
-import com.android.tools.r8.KotlinCompilerTool.KotlinCompiler;
+import com.android.tools.r8.KotlinTestParameters;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.ToolHelper;
-import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
import com.android.tools.r8.shaking.ProguardKeepAttributes;
import com.android.tools.r8.utils.StringUtils;
import com.android.tools.r8.utils.codeinspector.ClassSubject;
@@ -39,17 +37,16 @@
private final TestParameters parameters;
- @Parameterized.Parameters(name = "{0} target: {1}, kotlinc: {2}")
+ @Parameterized.Parameters(name = "{0}, {1}")
public static Collection<Object[]> data() {
return buildParameters(
getTestParameters().withCfRuntimes().build(),
- KotlinTargetVersion.values(),
- getKotlinCompilers());
+ getKotlinTestParameters().withAllCompilersAndTargetVersions().build());
}
public MetadataRewriteInFunctionTest(
- TestParameters parameters, KotlinTargetVersion targetVersion, KotlinCompiler kotlinc) {
- super(targetVersion, kotlinc);
+ TestParameters parameters, KotlinTestParameters kotlinParameters) {
+ super(kotlinParameters);
this.parameters = parameters;
}
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInFunctionWithDefaultValueTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInFunctionWithDefaultValueTest.java
index db348d0..746c690 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInFunctionWithDefaultValueTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInFunctionWithDefaultValueTest.java
@@ -3,7 +3,6 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.kotlin.metadata;
-import static com.android.tools.r8.ToolHelper.getKotlinCompilers;
import static com.android.tools.r8.utils.codeinspector.Matchers.isExtensionFunction;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresentAndNotRenamed;
@@ -12,10 +11,9 @@
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
-import com.android.tools.r8.KotlinCompilerTool.KotlinCompiler;
+import com.android.tools.r8.KotlinTestParameters;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.ToolHelper;
-import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
import com.android.tools.r8.shaking.ProguardKeepAttributes;
import com.android.tools.r8.utils.StringUtils;
import com.android.tools.r8.utils.codeinspector.ClassSubject;
@@ -39,17 +37,16 @@
private final TestParameters parameters;
- @Parameterized.Parameters(name = "{0}, target: {1}, kotlinc: {2}")
+ @Parameterized.Parameters(name = "{0}, {1}")
public static Collection<Object[]> data() {
return buildParameters(
getTestParameters().withCfRuntimes().build(),
- KotlinTargetVersion.values(),
- getKotlinCompilers());
+ getKotlinTestParameters().withAllCompilersAndTargetVersions().build());
}
public MetadataRewriteInFunctionWithDefaultValueTest(
- TestParameters parameters, KotlinTargetVersion targetVersion, KotlinCompiler kotlinc) {
- super(targetVersion, kotlinc);
+ TestParameters parameters, KotlinTestParameters kotlinParameters) {
+ super(kotlinParameters);
this.parameters = parameters;
}
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInFunctionWithVarargTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInFunctionWithVarargTest.java
index 65207dd..262d8a2 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInFunctionWithVarargTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInFunctionWithVarargTest.java
@@ -3,7 +3,6 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.kotlin.metadata;
-import static com.android.tools.r8.ToolHelper.getKotlinCompilers;
import static com.android.tools.r8.utils.codeinspector.Matchers.isExtensionFunction;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresentAndNotRenamed;
@@ -13,10 +12,9 @@
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
-import com.android.tools.r8.KotlinCompilerTool.KotlinCompiler;
+import com.android.tools.r8.KotlinTestParameters;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.ToolHelper;
-import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
import com.android.tools.r8.shaking.ProguardKeepAttributes;
import com.android.tools.r8.utils.StringUtils;
import com.android.tools.r8.utils.codeinspector.ClassSubject;
@@ -40,17 +38,16 @@
private final TestParameters parameters;
- @Parameterized.Parameters(name = "{0}, target: {1}, kotlinc: {2}")
+ @Parameterized.Parameters(name = "{0}, {1}")
public static Collection<Object[]> data() {
return buildParameters(
getTestParameters().withCfRuntimes().build(),
- KotlinTargetVersion.values(),
- getKotlinCompilers());
+ getKotlinTestParameters().withAllCompilersAndTargetVersions().build());
}
public MetadataRewriteInFunctionWithVarargTest(
- TestParameters parameters, KotlinTargetVersion targetVersion, KotlinCompiler kotlinc) {
- super(targetVersion, kotlinc);
+ TestParameters parameters, KotlinTestParameters kotlinParameters) {
+ super(kotlinParameters);
this.parameters = parameters;
}
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInLibraryTypeTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInLibraryTypeTest.java
index f3c59d9..d583ea7 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInLibraryTypeTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInLibraryTypeTest.java
@@ -3,7 +3,6 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.kotlin.metadata;
-import static com.android.tools.r8.ToolHelper.getKotlinCompilers;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresentAndNotRenamed;
import static org.hamcrest.CoreMatchers.anyOf;
@@ -11,10 +10,9 @@
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.Assert.assertTrue;
-import com.android.tools.r8.KotlinCompilerTool.KotlinCompiler;
+import com.android.tools.r8.KotlinTestParameters;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.ToolHelper;
-import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
import com.android.tools.r8.shaking.ProguardKeepAttributes;
import com.android.tools.r8.utils.StringUtils;
import com.android.tools.r8.utils.codeinspector.ClassSubject;
@@ -32,17 +30,16 @@
private final TestParameters parameters;
- @Parameterized.Parameters(name = "{0}, target: {1}, kotlinc: {2}")
+ @Parameterized.Parameters(name = "{0}, {1}")
public static Collection<Object[]> data() {
return buildParameters(
getTestParameters().withCfRuntimes().build(),
- KotlinTargetVersion.values(),
- getKotlinCompilers());
+ getKotlinTestParameters().withAllCompilersAndTargetVersions().build());
}
public MetadataRewriteInLibraryTypeTest(
- TestParameters parameters, KotlinTargetVersion targetVersion, KotlinCompiler kotlinc) {
- super(targetVersion, kotlinc);
+ TestParameters parameters, KotlinTestParameters kotlinParameters) {
+ super(kotlinParameters);
this.parameters = parameters;
}
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInMultifileClassTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInMultifileClassTest.java
index 3949d6e..6411cfc 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInMultifileClassTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInMultifileClassTest.java
@@ -3,7 +3,6 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.kotlin.metadata;
-import static com.android.tools.r8.ToolHelper.getKotlinCompilers;
import static com.android.tools.r8.utils.codeinspector.Matchers.isExtensionFunction;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresentAndNotRenamed;
@@ -16,10 +15,9 @@
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
-import com.android.tools.r8.KotlinCompilerTool.KotlinCompiler;
+import com.android.tools.r8.KotlinTestParameters;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.ToolHelper;
-import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
import com.android.tools.r8.ToolHelper.ProcessResult;
import com.android.tools.r8.shaking.ProguardKeepAttributes;
import com.android.tools.r8.utils.DescriptorUtils;
@@ -44,17 +42,16 @@
private final TestParameters parameters;
- @Parameterized.Parameters(name = "{0}, target: {1}, kotlinc: {2}")
+ @Parameterized.Parameters(name = "{0}, {1}")
public static Collection<Object[]> data() {
return buildParameters(
getTestParameters().withCfRuntimes().build(),
- KotlinTargetVersion.values(),
- getKotlinCompilers());
+ getKotlinTestParameters().withAllCompilersAndTargetVersions().build());
}
public MetadataRewriteInMultifileClassTest(
- TestParameters parameters, KotlinTargetVersion targetVersion, KotlinCompiler kotlinc) {
- super(targetVersion, kotlinc);
+ TestParameters parameters, KotlinTestParameters kotlinParameters) {
+ super(kotlinParameters);
this.parameters = parameters;
}
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInNestedClassTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInNestedClassTest.java
index 8bb4603..f2aa77d 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInNestedClassTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInNestedClassTest.java
@@ -3,7 +3,6 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.kotlin.metadata;
-import static com.android.tools.r8.ToolHelper.getKotlinCompilers;
import static com.android.tools.r8.utils.DescriptorUtils.descriptorToJavaType;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresentAndNotRenamed;
@@ -12,10 +11,9 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
-import com.android.tools.r8.KotlinCompilerTool.KotlinCompiler;
+import com.android.tools.r8.KotlinTestParameters;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.ToolHelper;
-import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
import com.android.tools.r8.shaking.ProguardKeepAttributes;
import com.android.tools.r8.utils.StringUtils;
import com.android.tools.r8.utils.codeinspector.ClassSubject;
@@ -34,17 +32,16 @@
private final TestParameters parameters;
- @Parameterized.Parameters(name = "{0}, target: {1}, kotlinc: {2}")
+ @Parameterized.Parameters(name = "{0}, {1}")
public static Collection<Object[]> data() {
return buildParameters(
getTestParameters().withCfRuntimes().build(),
- KotlinTargetVersion.values(),
- getKotlinCompilers());
+ getKotlinTestParameters().withAllCompilersAndTargetVersions().build());
}
public MetadataRewriteInNestedClassTest(
- TestParameters parameters, KotlinTargetVersion targetVersion, KotlinCompiler kotlinc) {
- super(targetVersion, kotlinc);
+ TestParameters parameters, KotlinTestParameters kotlinParameters) {
+ super(kotlinParameters);
this.parameters = parameters;
}
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInParameterTypeTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInParameterTypeTest.java
index f84fa48..abce684 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInParameterTypeTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInParameterTypeTest.java
@@ -3,17 +3,15 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.kotlin.metadata;
-import static com.android.tools.r8.ToolHelper.getKotlinCompilers;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresentAndNotRenamed;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresentAndRenamed;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.Assert.assertTrue;
-import com.android.tools.r8.KotlinCompilerTool.KotlinCompiler;
+import com.android.tools.r8.KotlinTestParameters;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.ToolHelper;
-import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
import com.android.tools.r8.shaking.ProguardKeepAttributes;
import com.android.tools.r8.utils.StringUtils;
import com.android.tools.r8.utils.codeinspector.ClassSubject;
@@ -32,17 +30,16 @@
private final TestParameters parameters;
- @Parameterized.Parameters(name = "{0}, target: {1}, kotlinc: {2}")
+ @Parameterized.Parameters(name = "{0}, {1}")
public static Collection<Object[]> data() {
return buildParameters(
getTestParameters().withCfRuntimes().build(),
- KotlinTargetVersion.values(),
- getKotlinCompilers());
+ getKotlinTestParameters().withAllCompilersAndTargetVersions().build());
}
public MetadataRewriteInParameterTypeTest(
- TestParameters parameters, KotlinTargetVersion targetVersion, KotlinCompiler kotlinc) {
- super(targetVersion, kotlinc);
+ TestParameters parameters, KotlinTestParameters kotlinParameters) {
+ super(kotlinParameters);
this.parameters = parameters;
}
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInPropertyTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInPropertyTest.java
index af4c9b6..693c7fc 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInPropertyTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInPropertyTest.java
@@ -3,7 +3,6 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.kotlin.metadata;
-import static com.android.tools.r8.ToolHelper.getKotlinCompilers;
import static com.android.tools.r8.utils.codeinspector.Matchers.isExtensionProperty;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresentAndNotRenamed;
@@ -14,10 +13,9 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
-import com.android.tools.r8.KotlinCompilerTool.KotlinCompiler;
+import com.android.tools.r8.KotlinTestParameters;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.ToolHelper;
-import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
import com.android.tools.r8.shaking.ProguardKeepAttributes;
import com.android.tools.r8.utils.StringUtils;
import com.android.tools.r8.utils.codeinspector.ClassSubject;
@@ -38,17 +36,16 @@
private final TestParameters parameters;
- @Parameterized.Parameters(name = "{0}, target: {1}, kotlinc: {2}")
+ @Parameterized.Parameters(name = "{0}, {1}")
public static Collection<Object[]> data() {
return buildParameters(
getTestParameters().withCfRuntimes().build(),
- KotlinTargetVersion.values(),
- getKotlinCompilers());
+ getKotlinTestParameters().withAllCompilersAndTargetVersions().build());
}
public MetadataRewriteInPropertyTest(
- TestParameters parameters, KotlinTargetVersion targetVersion, KotlinCompiler kotlinc) {
- super(targetVersion, kotlinc);
+ TestParameters parameters, KotlinTestParameters kotlinParameters) {
+ super(kotlinParameters);
this.parameters = parameters;
}
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInPropertyTypeTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInPropertyTypeTest.java
index 0113f19..cb18acb 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInPropertyTypeTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInPropertyTypeTest.java
@@ -3,17 +3,15 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.kotlin.metadata;
-import static com.android.tools.r8.ToolHelper.getKotlinCompilers;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresentAndNotRenamed;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresentAndRenamed;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.Assert.assertTrue;
-import com.android.tools.r8.KotlinCompilerTool.KotlinCompiler;
+import com.android.tools.r8.KotlinTestParameters;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.ToolHelper;
-import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
import com.android.tools.r8.shaking.ProguardKeepAttributes;
import com.android.tools.r8.utils.StringUtils;
import com.android.tools.r8.utils.codeinspector.ClassSubject;
@@ -32,17 +30,16 @@
private final TestParameters parameters;
- @Parameterized.Parameters(name = "{0}, target: {1}, kotlinc: {2}")
+ @Parameterized.Parameters(name = "{0}, {1}")
public static Collection<Object[]> data() {
return buildParameters(
getTestParameters().withCfRuntimes().build(),
- KotlinTargetVersion.values(),
- getKotlinCompilers());
+ getKotlinTestParameters().withAllCompilersAndTargetVersions().build());
}
public MetadataRewriteInPropertyTypeTest(
- TestParameters parameters, KotlinTargetVersion targetVersion, KotlinCompiler kotlinc) {
- super(targetVersion, kotlinc);
+ TestParameters parameters, KotlinTestParameters kotlinParameters) {
+ super(kotlinParameters);
this.parameters = parameters;
}
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInRenamedTypeTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInRenamedTypeTest.java
index a4a3a68..7badf74 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInRenamedTypeTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInRenamedTypeTest.java
@@ -3,7 +3,6 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.kotlin.metadata;
-import static com.android.tools.r8.ToolHelper.getKotlinCompilers;
import static com.android.tools.r8.utils.DescriptorUtils.getDescriptorFromKotlinClassifier;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresentAndNotRenamed;
@@ -12,10 +11,9 @@
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.Assert.assertEquals;
-import com.android.tools.r8.KotlinCompilerTool.KotlinCompiler;
+import com.android.tools.r8.KotlinTestParameters;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.ToolHelper;
-import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
import com.android.tools.r8.shaking.ProguardKeepAttributes;
import com.android.tools.r8.utils.codeinspector.AnnotationSubject;
import com.android.tools.r8.utils.codeinspector.ClassSubject;
@@ -33,17 +31,16 @@
private final TestParameters parameters;
- @Parameterized.Parameters(name = "{0}, target: {1}, kotlinc: {2}")
+ @Parameterized.Parameters(name = "{0}, {1}")
public static Collection<Object[]> data() {
return buildParameters(
getTestParameters().withAllRuntimesAndApiLevels().build(),
- KotlinTargetVersion.values(),
- getKotlinCompilers());
+ getKotlinTestParameters().withAllCompilersAndTargetVersions().build());
}
public MetadataRewriteInRenamedTypeTest(
- TestParameters parameters, KotlinTargetVersion targetVersion, KotlinCompiler kotlinc) {
- super(targetVersion, kotlinc);
+ TestParameters parameters, KotlinTestParameters kotlinParameters) {
+ super(kotlinParameters);
this.parameters = parameters;
}
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInReturnTypeTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInReturnTypeTest.java
index c8144f4..dbaf933 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInReturnTypeTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInReturnTypeTest.java
@@ -3,17 +3,15 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.kotlin.metadata;
-import static com.android.tools.r8.ToolHelper.getKotlinCompilers;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresentAndNotRenamed;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresentAndRenamed;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.Assert.assertTrue;
-import com.android.tools.r8.KotlinCompilerTool.KotlinCompiler;
+import com.android.tools.r8.KotlinTestParameters;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.ToolHelper;
-import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
import com.android.tools.r8.shaking.ProguardKeepAttributes;
import com.android.tools.r8.utils.StringUtils;
import com.android.tools.r8.utils.codeinspector.ClassSubject;
@@ -32,17 +30,16 @@
private final TestParameters parameters;
- @Parameterized.Parameters(name = "{0}, target: {1}, kotlinc: {2}")
+ @Parameterized.Parameters(name = "{0}, {1}")
public static Collection<Object[]> data() {
return buildParameters(
getTestParameters().withCfRuntimes().build(),
- KotlinTargetVersion.values(),
- getKotlinCompilers());
+ getKotlinTestParameters().withAllCompilersAndTargetVersions().build());
}
public MetadataRewriteInReturnTypeTest(
- TestParameters parameters, KotlinTargetVersion targetVersion, KotlinCompiler kotlinc) {
- super(targetVersion, kotlinc);
+ TestParameters parameters, KotlinTestParameters kotlinParameters) {
+ super(kotlinParameters);
this.parameters = parameters;
}
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInSealedClassNestedTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInSealedClassNestedTest.java
index db927a8..e479bba 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInSealedClassNestedTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInSealedClassNestedTest.java
@@ -3,12 +3,9 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.kotlin.metadata;
-import static com.android.tools.r8.ToolHelper.getKotlinCompilers;
-
-import com.android.tools.r8.KotlinCompilerTool.KotlinCompiler;
+import com.android.tools.r8.KotlinTestParameters;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.ToolHelper;
-import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
import com.android.tools.r8.shaking.ProguardKeepAttributes;
import com.android.tools.r8.utils.DescriptorUtils;
import com.android.tools.r8.utils.StringUtils;
@@ -31,17 +28,16 @@
private final TestParameters parameters;
- @Parameterized.Parameters(name = "{0}, target: {1}, kotlinc: {2}")
+ @Parameterized.Parameters(name = "{0}, {1}")
public static Collection<Object[]> data() {
return buildParameters(
getTestParameters().withCfRuntimes().build(),
- KotlinTargetVersion.values(),
- getKotlinCompilers());
+ getKotlinTestParameters().withAllCompilersAndTargetVersions().build());
}
public MetadataRewriteInSealedClassNestedTest(
- TestParameters parameters, KotlinTargetVersion targetVersion, KotlinCompiler kotlinc) {
- super(targetVersion, kotlinc);
+ TestParameters parameters, KotlinTestParameters kotlinParameters) {
+ super(kotlinParameters);
this.parameters = parameters;
}
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInSealedClassTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInSealedClassTest.java
index ef43f6e..5156b72 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInSealedClassTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInSealedClassTest.java
@@ -3,7 +3,6 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.kotlin.metadata;
-import static com.android.tools.r8.ToolHelper.getKotlinCompilers;
import static com.android.tools.r8.utils.DescriptorUtils.descriptorToJavaType;
import static com.android.tools.r8.utils.codeinspector.Matchers.isExtensionFunction;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
@@ -15,10 +14,9 @@
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotEquals;
-import com.android.tools.r8.KotlinCompilerTool.KotlinCompiler;
+import com.android.tools.r8.KotlinTestParameters;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.ToolHelper;
-import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
import com.android.tools.r8.ToolHelper.ProcessResult;
import com.android.tools.r8.shaking.ProguardKeepAttributes;
import com.android.tools.r8.utils.StringUtils;
@@ -39,17 +37,16 @@
private final TestParameters parameters;
- @Parameterized.Parameters(name = "{0}, target: {1}, kotlinc: {2}")
+ @Parameterized.Parameters(name = "{0}, {1}")
public static Collection<Object[]> data() {
return buildParameters(
getTestParameters().withCfRuntimes().build(),
- KotlinTargetVersion.values(),
- getKotlinCompilers());
+ getKotlinTestParameters().withAllCompilersAndTargetVersions().build());
}
public MetadataRewriteInSealedClassTest(
- TestParameters parameters, KotlinTargetVersion targetVersion, KotlinCompiler kotlinc) {
- super(targetVersion, kotlinc);
+ TestParameters parameters, KotlinTestParameters kotlinParameters) {
+ super(kotlinParameters);
this.parameters = parameters;
}
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInSuperTypeTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInSuperTypeTest.java
index 9b0da43..ccc94e5 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInSuperTypeTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInSuperTypeTest.java
@@ -3,7 +3,6 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.kotlin.metadata;
-import static com.android.tools.r8.ToolHelper.getKotlinCompilers;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresentAndNotRenamed;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresentAndRenamed;
@@ -11,10 +10,9 @@
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.Assert.assertTrue;
-import com.android.tools.r8.KotlinCompilerTool.KotlinCompiler;
+import com.android.tools.r8.KotlinTestParameters;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.ToolHelper;
-import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
import com.android.tools.r8.shaking.ProguardKeepAttributes;
import com.android.tools.r8.utils.StringUtils;
import com.android.tools.r8.utils.codeinspector.ClassSubject;
@@ -33,17 +31,16 @@
private final TestParameters parameters;
- @Parameterized.Parameters(name = "{0}, target: {1}, kotlinc: {2}")
+ @Parameterized.Parameters(name = "{0}, {1}")
public static Collection<Object[]> data() {
return buildParameters(
getTestParameters().withCfRuntimes().build(),
- KotlinTargetVersion.values(),
- getKotlinCompilers());
+ getKotlinTestParameters().withAllCompilersAndTargetVersions().build());
}
public MetadataRewriteInSuperTypeTest(
- TestParameters parameters, KotlinTargetVersion targetVersion, KotlinCompiler kotlinc) {
- super(targetVersion, kotlinc);
+ TestParameters parameters, KotlinTestParameters kotlinParameters) {
+ super(kotlinParameters);
this.parameters = parameters;
}
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInTypeAliasTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInTypeAliasTest.java
index e680d6e..98102c5 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInTypeAliasTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInTypeAliasTest.java
@@ -3,7 +3,6 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.kotlin.metadata;
-import static com.android.tools.r8.ToolHelper.getKotlinCompilers;
import static com.android.tools.r8.utils.codeinspector.Matchers.isDexClass;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresentAndNotRenamed;
@@ -13,10 +12,9 @@
import static junit.framework.TestCase.assertTrue;
import static org.hamcrest.MatcherAssert.assertThat;
-import com.android.tools.r8.KotlinCompilerTool.KotlinCompiler;
+import com.android.tools.r8.KotlinTestParameters;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.ToolHelper;
-import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
import com.android.tools.r8.kotlin.Kotlin.ClassClassifiers;
import com.android.tools.r8.shaking.ProguardKeepAttributes;
import com.android.tools.r8.utils.DescriptorUtils;
@@ -61,17 +59,16 @@
private final TestParameters parameters;
- @Parameterized.Parameters(name = "{0}, target: {1}, kotlinc: {2}")
+ @Parameterized.Parameters(name = "{0}, {1}")
public static Collection<Object[]> data() {
return buildParameters(
getTestParameters().withCfRuntimes().build(),
- KotlinTargetVersion.values(),
- getKotlinCompilers());
+ getKotlinTestParameters().withAllCompilersAndTargetVersions().build());
}
public MetadataRewriteInTypeAliasTest(
- TestParameters parameters, KotlinTargetVersion targetVersion, KotlinCompiler kotlinc) {
- super(targetVersion, kotlinc);
+ TestParameters parameters, KotlinTestParameters kotlinParameters) {
+ super(kotlinParameters);
this.parameters = parameters;
}
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInTypeArgumentsTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInTypeArgumentsTest.java
index acdda5b..097c832 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInTypeArgumentsTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInTypeArgumentsTest.java
@@ -3,7 +3,6 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.kotlin.metadata;
-import static com.android.tools.r8.ToolHelper.getKotlinCompilers;
import static com.android.tools.r8.utils.codeinspector.Matchers.isDexClass;
import static com.android.tools.r8.utils.codeinspector.Matchers.isExtensionFunction;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
@@ -12,10 +11,9 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
-import com.android.tools.r8.KotlinCompilerTool.KotlinCompiler;
+import com.android.tools.r8.KotlinTestParameters;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.ToolHelper;
-import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
import com.android.tools.r8.shaking.ProguardKeepAttributes;
import com.android.tools.r8.utils.StringUtils;
import com.android.tools.r8.utils.codeinspector.ClassSubject;
@@ -77,17 +75,16 @@
private final TestParameters parameters;
- @Parameterized.Parameters(name = "{0}, target: {1}, kotlinc: {2}")
+ @Parameterized.Parameters(name = "{0}, {1}")
public static Collection<Object[]> data() {
return buildParameters(
getTestParameters().withCfRuntimes().build(),
- KotlinTargetVersion.values(),
- getKotlinCompilers());
+ getKotlinTestParameters().withAllCompilersAndTargetVersions().build());
}
public MetadataRewriteInTypeArgumentsTest(
- TestParameters parameters, KotlinTargetVersion targetVersion, KotlinCompiler kotlinc) {
- super(targetVersion, kotlinc);
+ TestParameters parameters, KotlinTestParameters kotlinParameters) {
+ super(kotlinParameters);
this.parameters = parameters;
}
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInlinePropertyTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInlinePropertyTest.java
index ac232d7..d5e9e1a 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInlinePropertyTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInlinePropertyTest.java
@@ -4,15 +4,13 @@
package com.android.tools.r8.kotlin.metadata;
-import static com.android.tools.r8.ToolHelper.getKotlinCompilers;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.Assert.assertNull;
-import com.android.tools.r8.KotlinCompilerTool.KotlinCompiler;
+import com.android.tools.r8.KotlinTestParameters;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.ToolHelper;
-import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
import com.android.tools.r8.kotlin.KotlinMetadataWriter;
import com.android.tools.r8.shaking.ProguardKeepAttributes;
import com.android.tools.r8.utils.StringUtils;
@@ -34,17 +32,16 @@
private final String EXPECTED = StringUtils.lines("true", "false", "false", "true");
- @Parameterized.Parameters(name = "{0}, target: {1}, kotlinc: {2}")
+ @Parameterized.Parameters(name = "{0}, {1}")
public static Collection<Object[]> data() {
return buildParameters(
getTestParameters().withCfRuntimes().build(),
- KotlinTargetVersion.values(),
- getKotlinCompilers());
+ getKotlinTestParameters().withAllCompilersAndTargetVersions().build());
}
public MetadataRewriteInlinePropertyTest(
- TestParameters parameters, KotlinTargetVersion targetVersion, KotlinCompiler kotlinc) {
- super(targetVersion, kotlinc);
+ TestParameters parameters, KotlinTestParameters kotlinParameters) {
+ super(kotlinParameters);
this.parameters = parameters;
}
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteJvmStaticTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteJvmStaticTest.java
index 0558ebc..e03a72c 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteJvmStaticTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteJvmStaticTest.java
@@ -3,12 +3,11 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.kotlin.metadata;
-import static com.android.tools.r8.ToolHelper.getKotlinCompilers;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.Assert.assertEquals;
-import com.android.tools.r8.KotlinCompilerTool.KotlinCompiler;
+import com.android.tools.r8.KotlinTestParameters;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.ToolHelper;
import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
@@ -40,12 +39,18 @@
@Parameterized.Parameters(name = "{0}, kotlinc: {1}")
public static List<Object[]> data() {
- return buildParameters(getTestParameters().withCfRuntimes().build(), getKotlinCompilers());
+ // We are testing static methods on interfaces which requires java 8.
+ return buildParameters(
+ getTestParameters().withCfRuntimes().build(),
+ getKotlinTestParameters()
+ .withAllCompilers()
+ .withTargetVersion(KotlinTargetVersion.JAVA_8)
+ .build());
}
- public MetadataRewriteJvmStaticTest(TestParameters parameters, KotlinCompiler kotlinc) {
- // We are testing static methods on interfaces which requires java 8.
- super(KotlinTargetVersion.JAVA_8, kotlinc);
+ public MetadataRewriteJvmStaticTest(
+ TestParameters parameters, KotlinTestParameters kotlinParameters) {
+ super(kotlinParameters);
this.parameters = parameters;
}
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteKeepPathTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteKeepPathTest.java
index 056b18b..569d2b7 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteKeepPathTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteKeepPathTest.java
@@ -4,17 +4,15 @@
package com.android.tools.r8.kotlin.metadata;
-import static com.android.tools.r8.ToolHelper.getKotlinCompilers;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
import static org.hamcrest.CoreMatchers.equalTo;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.Assert.assertEquals;
-import com.android.tools.r8.KotlinCompilerTool.KotlinCompiler;
+import com.android.tools.r8.KotlinTestParameters;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestShrinkerBuilder;
import com.android.tools.r8.ToolHelper;
-import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
import com.android.tools.r8.utils.BooleanUtils;
import com.android.tools.r8.utils.codeinspector.ClassSubject;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
@@ -26,12 +24,11 @@
@RunWith(Parameterized.class)
public class MetadataRewriteKeepPathTest extends KotlinMetadataTestBase {
- @Parameterized.Parameters(name = "{0} target: {1}, kotlinc: {2}, keep: {3}")
+ @Parameterized.Parameters(name = "{0}, {1}, keep: {2}")
public static Collection<Object[]> data() {
return buildParameters(
getTestParameters().withCfRuntimes().build(),
- KotlinTargetVersion.values(),
- getKotlinCompilers(),
+ getKotlinTestParameters().withAllCompilersAndTargetVersions().build(),
BooleanUtils.values());
}
@@ -42,11 +39,8 @@
private final boolean keepMetadata;
public MetadataRewriteKeepPathTest(
- TestParameters parameters,
- KotlinTargetVersion targetVersion,
- KotlinCompiler kotlinc,
- boolean keepMetadata) {
- super(targetVersion, kotlinc);
+ TestParameters parameters, KotlinTestParameters kotlinParameters, boolean keepMetadata) {
+ super(kotlinParameters);
this.parameters = parameters;
this.keepMetadata = keepMetadata;
}
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteKeepTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteKeepTest.java
index 74159a6..c8dd873 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteKeepTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteKeepTest.java
@@ -4,14 +4,12 @@
package com.android.tools.r8.kotlin.metadata;
-import static com.android.tools.r8.ToolHelper.getKotlinCompilers;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
-import com.android.tools.r8.KotlinCompilerTool.KotlinCompiler;
+import com.android.tools.r8.KotlinTestParameters;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.ToolHelper;
-import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
import com.android.tools.r8.shaking.ProguardKeepAttributes;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
import com.android.tools.r8.utils.codeinspector.FoundClassSubject;
@@ -23,19 +21,17 @@
@RunWith(Parameterized.class)
public class MetadataRewriteKeepTest extends KotlinMetadataTestBase {
- @Parameterized.Parameters(name = "{0}, target: {1}, kotlinc: {2}")
+ @Parameterized.Parameters(name = "{0}, {1}")
public static Collection<Object[]> data() {
return buildParameters(
getTestParameters().withCfRuntimes().build(),
- KotlinTargetVersion.values(),
- getKotlinCompilers());
+ getKotlinTestParameters().withAllCompilersAndTargetVersions().build());
}
private final TestParameters parameters;
- public MetadataRewriteKeepTest(
- TestParameters parameters, KotlinTargetVersion targetVersion, KotlinCompiler kotlinc) {
- super(targetVersion, kotlinc);
+ public MetadataRewriteKeepTest(TestParameters parameters, KotlinTestParameters kotlinParameters) {
+ super(kotlinParameters);
this.parameters = parameters;
}
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewritePassThroughTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewritePassThroughTest.java
index 0b65ec3..99586b1 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewritePassThroughTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewritePassThroughTest.java
@@ -4,12 +4,9 @@
package com.android.tools.r8.kotlin.metadata;
-import static com.android.tools.r8.ToolHelper.getKotlinCompilers;
-
-import com.android.tools.r8.KotlinCompilerTool.KotlinCompiler;
+import com.android.tools.r8.KotlinTestParameters;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.ToolHelper;
-import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
import com.android.tools.r8.shaking.ProguardKeepAttributes;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
import java.util.Collection;
@@ -20,19 +17,18 @@
@RunWith(Parameterized.class)
public class MetadataRewritePassThroughTest extends KotlinMetadataTestBase {
- @Parameterized.Parameters(name = "{0}, target: {1}, kotlinc: {2}")
+ @Parameterized.Parameters(name = "{0}, {1}")
public static Collection<Object[]> data() {
return buildParameters(
getTestParameters().withCfRuntimes().build(),
- KotlinTargetVersion.values(),
- getKotlinCompilers());
+ getKotlinTestParameters().withAllCompilersAndTargetVersions().build());
}
private final TestParameters parameters;
public MetadataRewritePassThroughTest(
- TestParameters parameters, KotlinTargetVersion targetVersion, KotlinCompiler kotlinc) {
- super(targetVersion, kotlinc);
+ TestParameters parameters, KotlinTestParameters kotlinParameters) {
+ super(kotlinParameters);
this.parameters = parameters;
}
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewritePrunedObjectsTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewritePrunedObjectsTest.java
index 3d3386c..6d4391f 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewritePrunedObjectsTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewritePrunedObjectsTest.java
@@ -4,16 +4,14 @@
package com.android.tools.r8.kotlin.metadata;
-import static com.android.tools.r8.ToolHelper.getKotlinCompilers;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
import static junit.framework.TestCase.assertEquals;
import static org.hamcrest.CoreMatchers.not;
import static org.hamcrest.MatcherAssert.assertThat;
-import com.android.tools.r8.KotlinCompilerTool.KotlinCompiler;
+import com.android.tools.r8.KotlinTestParameters;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.ToolHelper;
-import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
import com.android.tools.r8.utils.DescriptorUtils;
import com.android.tools.r8.utils.StringUtils;
import com.android.tools.r8.utils.codeinspector.ClassSubject;
@@ -37,17 +35,16 @@
getKotlinFileInTest(DescriptorUtils.getBinaryNameFromJavaType(PKG_LIB), "lib"));
private final TestParameters parameters;
- @Parameterized.Parameters(name = "{0}, target: {1}, kotlinc: {2}")
+ @Parameterized.Parameters(name = "{0}, {1}")
public static Collection<Object[]> data() {
return buildParameters(
getTestParameters().withCfRuntimes().build(),
- KotlinTargetVersion.values(),
- getKotlinCompilers());
+ getKotlinTestParameters().withAllCompilersAndTargetVersions().build());
}
public MetadataRewritePrunedObjectsTest(
- TestParameters parameters, KotlinTargetVersion targetVersion, KotlinCompiler kotlinc) {
- super(targetVersion, kotlinc);
+ TestParameters parameters, KotlinTestParameters kotlinParameters) {
+ super(kotlinParameters);
this.parameters = parameters;
}
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataStripTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataStripTest.java
index 1a2cd73..8111567 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataStripTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataStripTest.java
@@ -3,7 +3,6 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.kotlin.metadata;
-import static com.android.tools.r8.ToolHelper.getKotlinCompilers;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresentAndNotRenamed;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresentAndRenamed;
@@ -11,11 +10,10 @@
import static org.hamcrest.CoreMatchers.not;
import static org.hamcrest.MatcherAssert.assertThat;
-import com.android.tools.r8.KotlinCompilerTool.KotlinCompiler;
+import com.android.tools.r8.KotlinTestParameters;
import com.android.tools.r8.R8TestRunResult;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.ToolHelper;
-import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
import com.android.tools.r8.shaking.ProguardKeepAttributes;
import com.android.tools.r8.utils.codeinspector.AnnotationSubject;
import com.android.tools.r8.utils.codeinspector.ClassSubject;
@@ -31,17 +29,15 @@
private final TestParameters parameters;
private static final String FOLDER = "lambdas_jstyle_runnable";
- @Parameterized.Parameters(name = "{0}, target: {1}, kotlinc: {2}")
+ @Parameterized.Parameters(name = "{0}, {1}")
public static Collection<Object[]> data() {
return buildParameters(
getTestParameters().withAllRuntimesAndApiLevels().build(),
- KotlinTargetVersion.values(),
- getKotlinCompilers());
+ getKotlinTestParameters().withAllCompilersAndTargetVersions().build());
}
- public MetadataStripTest(
- TestParameters parameters, KotlinTargetVersion targetVersion, KotlinCompiler kotlinc) {
- super(targetVersion, kotlinc);
+ public MetadataStripTest(TestParameters parameters, KotlinTestParameters kotlinParameters) {
+ super(kotlinParameters);
this.parameters = parameters;
}
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataVersionNumberBumpTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataVersionNumberBumpTest.java
index e23ac8f..a7886b2 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataVersionNumberBumpTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataVersionNumberBumpTest.java
@@ -9,9 +9,9 @@
import static org.junit.Assert.fail;
import static org.objectweb.asm.Opcodes.ASM7;
+import com.android.tools.r8.KotlinTestParameters;
import com.android.tools.r8.R8FullTestBuilder;
import com.android.tools.r8.TestParameters;
-import com.android.tools.r8.TestParametersCollection;
import com.android.tools.r8.ToolHelper;
import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
import com.android.tools.r8.graph.DexAnnotationElement;
@@ -25,6 +25,7 @@
import com.android.tools.r8.utils.codeinspector.FoundClassSubject;
import java.io.IOException;
import java.util.Arrays;
+import java.util.List;
import java.util.function.Consumer;
import java.util.stream.Collectors;
import org.junit.Test;
@@ -39,12 +40,18 @@
private final TestParameters parameters;
@Parameters(name = "{0}")
- public static TestParametersCollection data() {
- return getTestParameters().withAllRuntimesAndApiLevels().build();
+ public static List<Object[]> data() {
+ return buildParameters(
+ getTestParameters().withAllRuntimesAndApiLevels().build(),
+ getKotlinTestParameters()
+ .withCompiler(getKotlinC_1_3_72())
+ .withTargetVersion(KotlinTargetVersion.JAVA_8)
+ .build());
}
- public MetadataVersionNumberBumpTest(TestParameters parameters) {
- super(KotlinTargetVersion.JAVA_8, getKotlinC_1_3_72());
+ public MetadataVersionNumberBumpTest(
+ TestParameters parameters, KotlinTestParameters kotlinParameters) {
+ super(kotlinParameters);
this.parameters = parameters;
}
diff --git a/src/test/java/com/android/tools/r8/kotlin/reflection/KotlinReflectTest.java b/src/test/java/com/android/tools/r8/kotlin/reflection/KotlinReflectTest.java
index ac7eb23..ae80118 100644
--- a/src/test/java/com/android/tools/r8/kotlin/reflection/KotlinReflectTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/reflection/KotlinReflectTest.java
@@ -4,16 +4,14 @@
package com.android.tools.r8.kotlin.reflection;
-import static com.android.tools.r8.ToolHelper.getKotlinCompilers;
import static org.hamcrest.CoreMatchers.equalTo;
import static org.junit.Assume.assumeTrue;
import com.android.tools.r8.DexIndexedConsumer.ArchiveConsumer;
-import com.android.tools.r8.KotlinCompilerTool.KotlinCompiler;
import com.android.tools.r8.KotlinTestBase;
+import com.android.tools.r8.KotlinTestParameters;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.ToolHelper;
-import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
import com.android.tools.r8.shaking.ProguardKeepAttributes;
import com.android.tools.r8.utils.DescriptorUtils;
import com.android.tools.r8.utils.FileUtils;
@@ -29,7 +27,6 @@
public class KotlinReflectTest extends KotlinTestBase {
private final TestParameters parameters;
- private final KotlinTargetVersion targetVersion;
private static final String EXPECTED_OUTPUT = "Hello World!";
private static final String PKG = KotlinReflectTest.class.getPackage().getName();
private static final KotlinCompileMemoizer compiledJars =
@@ -40,19 +37,16 @@
DescriptorUtils.getBinaryNameFromJavaType(PKG),
"SimpleReflect" + FileUtils.KT_EXTENSION));
- @Parameters(name = "{0}, target: {1}, kotlinc: {2}")
+ @Parameters(name = "{0}, {1}")
public static List<Object[]> data() {
return buildParameters(
getTestParameters().withAllRuntimesAndApiLevels().build(),
- KotlinTargetVersion.values(),
- getKotlinCompilers());
+ getKotlinTestParameters().withAllCompilersAndTargetVersions().build());
}
- public KotlinReflectTest(
- TestParameters parameters, KotlinTargetVersion targetVersion, KotlinCompiler kotlinc) {
- super(targetVersion, kotlinc);
+ public KotlinReflectTest(TestParameters parameters, KotlinTestParameters kotlinParameters) {
+ super(kotlinParameters);
this.parameters = parameters;
- this.targetVersion = targetVersion;
}
@Test
diff --git a/src/test/java/com/android/tools/r8/kotlin/sealed/SealedClassTest.java b/src/test/java/com/android/tools/r8/kotlin/sealed/SealedClassTest.java
index 92b6b97..47f9463 100644
--- a/src/test/java/com/android/tools/r8/kotlin/sealed/SealedClassTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/sealed/SealedClassTest.java
@@ -6,15 +6,13 @@
import static com.android.tools.r8.DiagnosticsMatcher.diagnosticMessage;
import static com.android.tools.r8.ToolHelper.getFilesInTestFolderRelativeToClass;
-import static com.android.tools.r8.ToolHelper.getKotlinCompilers;
import static org.hamcrest.CoreMatchers.containsString;
import com.android.tools.r8.CompilationFailedException;
-import com.android.tools.r8.KotlinCompilerTool.KotlinCompiler;
import com.android.tools.r8.KotlinTestBase;
+import com.android.tools.r8.KotlinTestParameters;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.ToolHelper;
-import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
import java.io.IOException;
import java.nio.file.Path;
import java.util.Collection;
@@ -33,17 +31,15 @@
private final TestParameters parameters;
- @Parameters(name = "{0}")
+ @Parameters(name = "{0}, {1}")
public static List<Object[]> data() {
return buildParameters(
getTestParameters().withAllRuntimesAndApiLevels().build(),
- KotlinTargetVersion.values(),
- getKotlinCompilers());
+ getKotlinTestParameters().withAllCompilersAndTargetVersions().build());
}
- public SealedClassTest(
- TestParameters parameters, KotlinTargetVersion targetVersion, KotlinCompiler kotlinc) {
- super(targetVersion, kotlinc);
+ public SealedClassTest(TestParameters parameters, KotlinTestParameters kotlinParameters) {
+ super(kotlinParameters);
this.parameters = parameters;
}
diff --git a/src/test/java/com/android/tools/r8/maindexlist/MainDexListFromGenerateMainDexInliningSpuriousRootTest.java b/src/test/java/com/android/tools/r8/maindexlist/MainDexListFromGenerateMainDexInliningSpuriousRootTest.java
new file mode 100644
index 0000000..5f21fee
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/maindexlist/MainDexListFromGenerateMainDexInliningSpuriousRootTest.java
@@ -0,0 +1,152 @@
+// Copyright (c) 2021, 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.maindexlist;
+
+import static com.android.tools.r8.utils.codeinspector.CodeMatchers.invokesMethod;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertEquals;
+
+import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.NoHorizontalClassMerging;
+import com.android.tools.r8.R8TestCompileResult;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.references.ClassReference;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import com.android.tools.r8.utils.codeinspector.MethodSubject;
+import com.google.common.collect.ImmutableSet;
+import java.util.List;
+import org.junit.BeforeClass;
+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 MainDexListFromGenerateMainDexInliningSpuriousRootTest extends TestBase {
+
+ private static List<ClassReference> mainDexList;
+ private final TestParameters parameters;
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters()
+ .withDexRuntimes()
+ .withApiLevelsEndingAtExcluding(apiLevelWithNativeMultiDexSupport())
+ .build();
+ }
+
+ @BeforeClass
+ public static void setup() throws Exception {
+ mainDexList =
+ testForMainDexListGenerator(getStaticTemp())
+ .addInnerClasses(MainDexListFromGenerateMainDexInliningSpuriousRootTest.class)
+ .addLibraryFiles(ToolHelper.getMostRecentAndroidJar())
+ .addMainDexRules(
+ "-keep class " + Main.class.getTypeName() + " {",
+ " public static void main(java.lang.String[]);",
+ "}")
+ .run()
+ .getMainDexList();
+ }
+
+ public MainDexListFromGenerateMainDexInliningSpuriousRootTest(TestParameters parameters) {
+ this.parameters = parameters;
+ }
+
+ @Test
+ public void test() throws Exception {
+ // The generated main dex list should contain Main (which is a root) and A (which is a direct
+ // dependency of Main).
+ assertEquals(2, mainDexList.size());
+ assertEquals(A.class.getTypeName(), mainDexList.get(0).getTypeName());
+ assertEquals(Main.class.getTypeName(), mainDexList.get(1).getTypeName());
+ R8TestCompileResult compileResult =
+ testForR8(parameters.getBackend())
+ .addInnerClasses(getClass())
+ .addInliningAnnotations()
+ .addKeepClassAndMembersRules(Main.class)
+ .addKeepMainRule(Main2.class)
+ .addMainDexListClassReferences(mainDexList)
+ .addMainDexRules(
+ "-keep class " + Main2.class.getTypeName() + " {",
+ " public static void main(java.lang.String[]);",
+ "}")
+ .collectMainDexClasses()
+ .enableInliningAnnotations()
+ .enableNoHorizontalClassMergingAnnotations()
+ .setMinApi(parameters.getApiLevel())
+ .noMinification()
+ .compile();
+ CodeInspector inspector = compileResult.inspector();
+ ClassSubject mainClassSubject = inspector.clazz(Main.class);
+ assertThat(mainClassSubject, isPresent());
+ MethodSubject fooMethodSubject = mainClassSubject.uniqueMethodWithName("foo");
+ assertThat(fooMethodSubject, isPresent());
+ ClassSubject main2ClassSubject = inspector.clazz(Main2.class);
+ assertThat(main2ClassSubject, isPresent());
+ ClassSubject aClassSubject = inspector.clazz(A.class);
+ assertThat(aClassSubject, isPresent());
+ MethodSubject barMethodSubject = aClassSubject.uniqueMethodWithName("bar");
+ assertThat(barMethodSubject, isPresent());
+ ClassSubject bClassSubject = inspector.clazz(B.class);
+ assertThat(bClassSubject, isPresent());
+ MethodSubject bazMethodSubject = bClassSubject.uniqueMethodWithName("baz");
+ assertThat(bazMethodSubject, isPresent());
+ assertThat(fooMethodSubject, invokesMethod(barMethodSubject));
+ assertThat(barMethodSubject, invokesMethod(bazMethodSubject));
+ assertEquals(
+ ImmutableSet.of(
+ mainClassSubject.getFinalName(),
+ main2ClassSubject.getFinalName(),
+ aClassSubject.getFinalName()),
+ compileResult.getMainDexClasses());
+ }
+
+ static class Main {
+
+ public static void main(String[] args) {
+ System.out.println("Main.main()");
+ }
+
+ static void foo() {
+ A.bar();
+ }
+ }
+
+ static class Main2 {
+
+ public static void main(String[] args) {
+ if (getFalse()) {
+ A.bar();
+ }
+ }
+
+ static boolean getFalse() {
+ return false;
+ }
+ }
+
+ @NoHorizontalClassMerging
+ static class A {
+ // Must not be inlined into Main.foo(), since that would cause B to become direct dependence of
+ // Main without ending up in the main dex.
+ static void bar() {
+ B.baz();
+ }
+ }
+
+ @NoHorizontalClassMerging
+ static class B {
+
+ @NeverInline
+ static void baz() {
+ System.out.println("B.baz");
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/maindexlist/MainDexListFromGenerateMainDexInliningTest.java b/src/test/java/com/android/tools/r8/maindexlist/MainDexListFromGenerateMainDexInliningTest.java
index a76d8cc..98ec426 100644
--- a/src/test/java/com/android/tools/r8/maindexlist/MainDexListFromGenerateMainDexInliningTest.java
+++ b/src/test/java/com/android/tools/r8/maindexlist/MainDexListFromGenerateMainDexInliningTest.java
@@ -5,7 +5,6 @@
package com.android.tools.r8.maindexlist;
import static com.android.tools.r8.utils.codeinspector.CodeMatchers.invokesMethod;
-import static com.android.tools.r8.utils.codeinspector.Matchers.isAbsent;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.Assert.assertEquals;
@@ -21,6 +20,7 @@
import com.android.tools.r8.utils.codeinspector.ClassSubject;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
import com.android.tools.r8.utils.codeinspector.MethodSubject;
+import com.google.common.collect.ImmutableSet;
import java.util.List;
import org.junit.BeforeClass;
import org.junit.Test;
@@ -90,12 +90,10 @@
assertThat(fooMethodSubject, isPresent());
ClassSubject aClassSubject = inspector.clazz(A.class);
- // TODO(b/178353726): Should be present, but was inlined.
- assertThat(aClassSubject, isAbsent());
+ assertThat(aClassSubject, isPresent());
MethodSubject barMethodSubject = aClassSubject.uniqueMethodWithName("bar");
- // TODO(b/178353726): Should be present, but was inlined.
- assertThat(barMethodSubject, isAbsent());
+ assertThat(barMethodSubject, isPresent());
ClassSubject bClassSubject = inspector.clazz(B.class);
assertThat(bClassSubject, isPresent());
@@ -103,16 +101,13 @@
MethodSubject bazMethodSubject = bClassSubject.uniqueMethodWithName("baz");
assertThat(bazMethodSubject, isPresent());
- // TODO(b/178353726): foo() should invoke bar() and bar() should invoke baz().
- assertThat(fooMethodSubject, invokesMethod(bazMethodSubject));
+ assertThat(fooMethodSubject, invokesMethod(barMethodSubject));
+ assertThat(barMethodSubject, invokesMethod(bazMethodSubject));
- // TODO(b/178353726): Main is the only class guaranteed to be in the main dex, but it has a
- // direct reference to B.
- compileResult.inspectMainDexClasses(
- mainDexClasses -> {
- assertEquals(1, mainDexClasses.size());
- assertEquals(mainClassSubject.getFinalName(), mainDexClasses.iterator().next());
- });
+ // The main dex classes should be the same as the input main dex list.
+ assertEquals(
+ ImmutableSet.of(mainClassSubject.getFinalName(), aClassSubject.getFinalName()),
+ compileResult.getMainDexClasses());
}
static class Main {
@@ -122,8 +117,8 @@
}
static void foo() {
- // TODO(b/178353726): Should not allow inlining bar into foo(), since that adds B as a direct
- // dependence, and we don't include the direct dependencies of main dex list classes.
+ // Should not allow inlining bar into foo(), since that adds B as a direct
+ // dependence, and we don't include the direct dependencies of main dex list classes.
A.bar();
}
}
diff --git a/src/test/java/com/android/tools/r8/maindexlist/MainDexListFromGenerateMainDexInliningWithTracingTest.java b/src/test/java/com/android/tools/r8/maindexlist/MainDexListFromGenerateMainDexInliningWithTracingTest.java
new file mode 100644
index 0000000..f5971f9
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/maindexlist/MainDexListFromGenerateMainDexInliningWithTracingTest.java
@@ -0,0 +1,158 @@
+// Copyright (c) 2021, 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.maindexlist;
+
+import static com.android.tools.r8.utils.codeinspector.CodeMatchers.invokesMethod;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertEquals;
+
+import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.NoHorizontalClassMerging;
+import com.android.tools.r8.R8TestCompileResult;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.references.ClassReference;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import com.android.tools.r8.utils.codeinspector.MethodSubject;
+import com.google.common.collect.ImmutableSet;
+import java.util.List;
+import org.junit.BeforeClass;
+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 MainDexListFromGenerateMainDexInliningWithTracingTest extends TestBase {
+
+ private static List<ClassReference> mainDexList;
+
+ private final TestParameters parameters;
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters()
+ .withDexRuntimes()
+ .withApiLevelsEndingAtExcluding(apiLevelWithNativeMultiDexSupport())
+ .build();
+ }
+
+ @BeforeClass
+ public static void setup() throws Exception {
+ mainDexList =
+ testForMainDexListGenerator(getStaticTemp())
+ .addInnerClasses(MainDexListFromGenerateMainDexInliningWithTracingTest.class)
+ .addLibraryFiles(ToolHelper.getMostRecentAndroidJar())
+ .addMainDexRules(
+ "-keep class " + Main.class.getTypeName() + " {",
+ " public static void main(java.lang.String[]);",
+ "}")
+ .run()
+ .getMainDexList();
+ }
+
+ public MainDexListFromGenerateMainDexInliningWithTracingTest(TestParameters parameters) {
+ this.parameters = parameters;
+ }
+
+ @Test
+ public void test() throws Exception {
+ // The generated main dex list should contain Main (which is a root) and A (which is a direct
+ // dependency of Main).
+ assertEquals(2, mainDexList.size());
+ assertEquals(A.class.getTypeName(), mainDexList.get(0).getTypeName());
+ assertEquals(Main.class.getTypeName(), mainDexList.get(1).getTypeName());
+
+ R8TestCompileResult compileResult =
+ testForR8(parameters.getBackend())
+ .addInnerClasses(getClass())
+ .addInliningAnnotations()
+ .addKeepClassAndMembersRules(Main.class)
+ .addMainDexListClassReferences(mainDexList)
+ .addMainDexRules(
+ "-keep class " + Main.class.getTypeName() + " {",
+ " public static void foo(java.lang.String[]);",
+ "}")
+ .collectMainDexClasses()
+ .enableInliningAnnotations()
+ .enableNoHorizontalClassMergingAnnotations()
+ .noMinification()
+ .setMinApi(parameters.getApiLevel())
+ .compile();
+
+ CodeInspector inspector = compileResult.inspector();
+ ClassSubject mainClassSubject = inspector.clazz(Main.class);
+ assertThat(mainClassSubject, isPresent());
+
+ MethodSubject fooMethodSubject = mainClassSubject.uniqueMethodWithName("foo");
+ assertThat(fooMethodSubject, isPresent());
+
+ MethodSubject notCalledAtStartupMethodSubject =
+ mainClassSubject.uniqueMethodWithName("notCalledAtStartup");
+ assertThat(notCalledAtStartupMethodSubject, isPresent());
+
+ ClassSubject aClassSubject = inspector.clazz(A.class);
+ assertThat(aClassSubject, isPresent());
+
+ MethodSubject barMethodSubject = aClassSubject.uniqueMethodWithName("bar");
+ assertThat(barMethodSubject, isPresent());
+
+ ClassSubject bClassSubject = inspector.clazz(B.class);
+ assertThat(bClassSubject, isPresent());
+
+ MethodSubject bazMethodSubject = bClassSubject.uniqueMethodWithName("baz");
+ assertThat(bazMethodSubject, isPresent());
+
+ assertThat(notCalledAtStartupMethodSubject, invokesMethod(barMethodSubject));
+ assertThat(barMethodSubject, invokesMethod(bazMethodSubject));
+
+ // The main dex classes should be the same as the input main dex list.
+ assertEquals(
+ ImmutableSet.of(mainClassSubject.getFinalName(), aClassSubject.getFinalName()),
+ compileResult.getMainDexClasses());
+ }
+
+ static class Main {
+
+ public static void main(String[] args) {
+ System.out.println("Main.main()");
+ }
+
+ public static void notCalledAtStartup() {
+ // Should not allow inlining bar into notCalledAtStartup(), since that adds B as a direct
+ // dependence, and we don't include the direct dependencies of main dex list classes.
+ new A().bar();
+ }
+
+ // This method is traced for main dex when running with R8, to add A as a dependency.
+ static A foo(A a) {
+ if (a != null) {
+ System.out.println("Hello World");
+ }
+ return a;
+ }
+ }
+
+ @NoHorizontalClassMerging
+ static class A {
+
+ static void bar() {
+ B.baz();
+ }
+ }
+
+ @NoHorizontalClassMerging
+ static class B {
+
+ @NeverInline
+ static void baz() {
+ System.out.println("B.baz");
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/maindexlist/MainDexListFromGenerateMainHorizontalMergingTest.java b/src/test/java/com/android/tools/r8/maindexlist/MainDexListFromGenerateMainHorizontalMergingTest.java
index 867b1ec..c2c96ec 100644
--- a/src/test/java/com/android/tools/r8/maindexlist/MainDexListFromGenerateMainHorizontalMergingTest.java
+++ b/src/test/java/com/android/tools/r8/maindexlist/MainDexListFromGenerateMainHorizontalMergingTest.java
@@ -5,7 +5,6 @@
package com.android.tools.r8.maindexlist;
import static com.android.tools.r8.utils.codeinspector.CodeMatchers.invokesMethod;
-import static com.android.tools.r8.utils.codeinspector.Matchers.isAbsent;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
import static org.hamcrest.CoreMatchers.containsString;
import static org.hamcrest.MatcherAssert.assertThat;
@@ -14,10 +13,12 @@
import com.android.tools.r8.NeverClassInline;
import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.R8FullTestBuilder;
import com.android.tools.r8.R8TestCompileResult;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.ThrowableConsumer;
import com.android.tools.r8.ToolHelper;
import com.android.tools.r8.references.ClassReference;
import com.android.tools.r8.references.TypeReference;
@@ -38,7 +39,6 @@
public class MainDexListFromGenerateMainHorizontalMergingTest extends TestBase {
private static List<ClassReference> mainDexList;
-
private final TestParameters parameters;
@Parameters(name = "{0}")
@@ -66,30 +66,45 @@
}
@Test
- public void test() throws Exception {
+ public void testMainDexList() throws Exception {
assertEquals(3, mainDexList.size());
Set<String> mainDexReferences =
mainDexList.stream().map(TypeReference::getTypeName).collect(Collectors.toSet());
assertTrue(mainDexReferences.contains(A.class.getTypeName()));
assertTrue(mainDexReferences.contains(B.class.getTypeName()));
assertTrue(mainDexReferences.contains(Main.class.getTypeName()));
+ runTest(builder -> builder.addMainDexListClassReferences(mainDexList));
+ }
- R8TestCompileResult compileResult =
- testForR8(parameters.getBackend())
- .addInnerClasses(getClass())
- .addInliningAnnotations()
- .addKeepClassAndMembersRules(Main.class, Outside.class)
- .addMainDexListClassReferences(mainDexList)
- .collectMainDexClasses()
- .enableInliningAnnotations()
- .enableNeverClassInliningAnnotations()
- .setMinApi(parameters.getApiLevel())
- .addHorizontallyMergedClassesInspector(
- horizontallyMergedClassesInspector -> {
- horizontallyMergedClassesInspector.assertMergedInto(B.class, A.class);
- })
- .compile();
+ @Test
+ public void testMainDexTracing() throws Exception {
+ runTest(
+ builder ->
+ builder.addMainDexRules(
+ "-keep class " + Main.class.getTypeName() + " { public static void foo(); }"));
+ }
+ private void runTest(ThrowableConsumer<R8FullTestBuilder> testBuilder) throws Exception {
+ testForR8(parameters.getBackend())
+ .addInnerClasses(getClass())
+ .addInliningAnnotations()
+ .addKeepClassAndMembersRules(Main.class, Outside.class)
+ .collectMainDexClasses()
+ .enableInliningAnnotations()
+ .enableNeverClassInliningAnnotations()
+ .setMinApi(parameters.getApiLevel())
+ .addHorizontallyMergedClassesInspector(
+ horizontallyMergedClassesInspector -> {
+ horizontallyMergedClassesInspector.assertClassesNotMerged(B.class, A.class);
+ })
+ .apply(testBuilder)
+ .compile()
+ .apply(this::inspectCompileResult)
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutputThatMatches(containsString(Outside.class.getTypeName()));
+ }
+
+ private void inspectCompileResult(R8TestCompileResult compileResult) throws Exception {
CodeInspector inspector = compileResult.inspector();
ClassSubject mainClassSubject = inspector.clazz(Main.class);
assertThat(mainClassSubject, isPresent());
@@ -100,9 +115,8 @@
ClassSubject aClassSubject = inspector.clazz(A.class);
assertThat(aClassSubject, isPresent());
- // TODO(b/178460068): Should be present, but was merged with A.
ClassSubject bClassSubject = inspector.clazz(B.class);
- assertThat(bClassSubject, isAbsent());
+ assertThat(bClassSubject, isPresent());
MethodSubject fooASubject = aClassSubject.uniqueMethodWithName("foo");
assertThat(fooASubject, isPresent());
@@ -112,19 +126,16 @@
compileResult.inspectMainDexClasses(
mainDexClasses -> {
assertEquals(
- ImmutableSet.of(mainClassSubject.getFinalName(), aClassSubject.getFinalName()),
+ ImmutableSet.of(
+ mainClassSubject.getFinalName(),
+ aClassSubject.getFinalName(),
+ bClassSubject.getFinalName()),
mainDexClasses);
});
-
- compileResult
- .run(parameters.getRuntime(), Main.class)
- .assertSuccessWithOutputThatMatches(containsString(Outside.class.getTypeName()));
}
static class Main {
- // public static B b;
-
public static void main(String[] args) {
B.print();
}
diff --git a/src/test/java/com/android/tools/r8/maindexlist/MainDexListFromGenerateMainVerticalMergingTest.java b/src/test/java/com/android/tools/r8/maindexlist/MainDexListFromGenerateMainVerticalMergingTest.java
index e3d49f5..ec50d1e 100644
--- a/src/test/java/com/android/tools/r8/maindexlist/MainDexListFromGenerateMainVerticalMergingTest.java
+++ b/src/test/java/com/android/tools/r8/maindexlist/MainDexListFromGenerateMainVerticalMergingTest.java
@@ -5,7 +5,6 @@
package com.android.tools.r8.maindexlist;
import static com.android.tools.r8.utils.codeinspector.CodeMatchers.invokesMethod;
-import static com.android.tools.r8.utils.codeinspector.Matchers.isAbsent;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.Assert.assertEquals;
@@ -13,16 +12,17 @@
import com.android.tools.r8.NeverClassInline;
import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.R8FullTestBuilder;
import com.android.tools.r8.R8TestCompileResult;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.ThrowableConsumer;
import com.android.tools.r8.ToolHelper;
import com.android.tools.r8.references.ClassReference;
import com.android.tools.r8.references.TypeReference;
import com.android.tools.r8.utils.codeinspector.ClassSubject;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
-import com.android.tools.r8.utils.codeinspector.FieldSubject;
import com.android.tools.r8.utils.codeinspector.MethodSubject;
import com.google.common.collect.ImmutableSet;
import java.util.List;
@@ -66,23 +66,34 @@
}
@Test
- public void test() throws Exception {
+ public void testMainDexList() throws Exception {
assertEquals(3, mainDexList.size());
Set<String> mainDexReferences =
mainDexList.stream().map(TypeReference::getTypeName).collect(Collectors.toSet());
assertTrue(mainDexReferences.contains(A.class.getTypeName()));
assertTrue(mainDexReferences.contains(B.class.getTypeName()));
assertTrue(mainDexReferences.contains(Main.class.getTypeName()));
+ runTest(builder -> builder.addMainDexListClassReferences(mainDexList));
+ }
+ @Test
+ public void testMainTracing() throws Exception {
+ runTest(
+ builder ->
+ builder.addMainDexRules(
+ "-keep class " + Main.class.getTypeName() + " { public static void foo(); }"));
+ }
+
+ private void runTest(ThrowableConsumer<R8FullTestBuilder> testBuilder) throws Exception {
R8TestCompileResult compileResult =
testForR8(parameters.getBackend())
.addInnerClasses(getClass())
.addInliningAnnotations()
.addKeepClassAndMembersRules(Main.class, Outside.class)
- .addMainDexListClassReferences(mainDexList)
.collectMainDexClasses()
.enableInliningAnnotations()
.enableNeverClassInliningAnnotations()
+ .apply(testBuilder)
.setMinApi(parameters.getApiLevel())
.compile();
@@ -94,25 +105,23 @@
assertThat(fooMethodSubject, isPresent());
ClassSubject aClassSubject = inspector.clazz(A.class);
- // TODO(b/178460068): Should be present, but was merged with B.
- assertThat(aClassSubject, isAbsent());
+ assertThat(aClassSubject, isPresent());
+
+ MethodSubject fooAMethodSubject = aClassSubject.uniqueMethodWithName("foo");
+ assertThat(fooAMethodSubject, isPresent());
ClassSubject bClassSubject = inspector.clazz(B.class);
assertThat(bClassSubject, isPresent());
- FieldSubject outsideFieldSubject = bClassSubject.uniqueFieldWithName("outsideField");
- assertThat(outsideFieldSubject, isPresent());
+ assertThat(fooMethodSubject, invokesMethod(fooAMethodSubject));
- MethodSubject fooBMethodSubject = bClassSubject.uniqueMethodWithName("foo");
- assertThat(fooBMethodSubject, isPresent());
-
- assertThat(fooMethodSubject, invokesMethod(fooBMethodSubject));
-
- // TODO(b/178460068): B should not be in main dex.
compileResult.inspectMainDexClasses(
mainDexClasses -> {
assertEquals(
- ImmutableSet.of(mainClassSubject.getFinalName(), bClassSubject.getFinalName()),
+ ImmutableSet.of(
+ mainClassSubject.getFinalName(),
+ aClassSubject.getFinalName(),
+ bClassSubject.getFinalName()),
mainDexClasses);
});
diff --git a/src/test/java/com/android/tools/r8/maindexlist/MainDexListInliningTest.java b/src/test/java/com/android/tools/r8/maindexlist/MainDexListInliningTest.java
index 82f5e7d..09df9d4 100644
--- a/src/test/java/com/android/tools/r8/maindexlist/MainDexListInliningTest.java
+++ b/src/test/java/com/android/tools/r8/maindexlist/MainDexListInliningTest.java
@@ -4,7 +4,6 @@
package com.android.tools.r8.maindexlist;
-import static com.android.tools.r8.utils.codeinspector.Matchers.isAbsent;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.Assert.assertFalse;
@@ -55,10 +54,9 @@
ClassSubject mainClassSubject = inspector.clazz(Main.class);
assertThat(mainClassSubject, isPresent());
- // A is absent due to inlining.
- // TODO(b/178353726): Inlining should be prohibited.
+ // A is not allowed to be inlined and is therefore present.
ClassSubject aClassSubject = inspector.clazz(A.class);
- assertThat(aClassSubject, isAbsent());
+ assertThat(aClassSubject, isPresent());
// B should be referenced from Main.main.
ClassSubject bClassSubject = inspector.clazz(B.class);
@@ -67,6 +65,9 @@
compileResult.inspectMainDexClasses(
mainDexClasses -> {
assertTrue(mainDexClasses.contains(mainClassSubject.getFinalName()));
+ // Since we passed a main-dex list the traced references A and B are not automagically
+ // included.
+ assertFalse(mainDexClasses.contains(aClassSubject.getFinalName()));
assertFalse(mainDexClasses.contains(bClassSubject.getFinalName()));
});
}
diff --git a/src/test/java/com/android/tools/r8/maindexlist/MainDexListMergeInRootTest.java b/src/test/java/com/android/tools/r8/maindexlist/MainDexListMergeInRootTest.java
index 7041c75..9451f9a 100644
--- a/src/test/java/com/android/tools/r8/maindexlist/MainDexListMergeInRootTest.java
+++ b/src/test/java/com/android/tools/r8/maindexlist/MainDexListMergeInRootTest.java
@@ -4,17 +4,13 @@
package com.android.tools.r8.maindexlist;
-import static com.android.tools.r8.DiagnosticsMatcher.diagnosticMessage;
-import static com.android.tools.r8.utils.codeinspector.AssertUtils.assertFailsCompilation;
-import static org.hamcrest.CoreMatchers.containsString;
-import static org.junit.Assume.assumeTrue;
-
import com.android.tools.r8.NeverClassInline;
import com.android.tools.r8.NeverInline;
import com.android.tools.r8.NoHorizontalClassMerging;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.utils.codeinspector.HorizontallyMergedClassesInspector;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
@@ -27,7 +23,10 @@
@Parameters(name = "{0}")
public static TestParametersCollection data() {
- return getTestParameters().withDexRuntimes().withAllApiLevels().build();
+ return getTestParameters()
+ .withDexRuntimes()
+ .withApiLevelsEndingAtExcluding(apiLevelWithNativeMultiDexSupport())
+ .build();
}
public MainDexListMergeInRootTest(TestParameters parameters) {
@@ -35,35 +34,26 @@
}
@Test
- public void testMainDexTracing() {
- assumeTrue(parameters.getDexRuntimeVersion().isDalvik());
- assertFailsCompilation(
- () ->
- testForR8(parameters.getBackend())
- .addProgramClasses(OutsideMainDex.class, InsideA.class, InsideB.class, Main.class)
- .addKeepClassAndMembersRules(Main.class)
- .setMinApi(parameters.getApiLevel())
- .enableNeverClassInliningAnnotations()
- .enableNoHorizontalClassMergingAnnotations()
- .enableInliningAnnotations()
- .noMinification()
- .addMainDexRules(
- "-keep class "
- + Main.class.getTypeName()
- + " { public static void main(***); }")
- .addOptionsModification(
- options -> {
- options.testing.checkForNotExpandingMainDexTracingResult = true;
- })
- .compileWithExpectedDiagnostics(
- diagnostics -> {
- diagnostics.assertErrorsMatch(
- diagnosticMessage(
- containsString(
- "Class com.android.tools.r8.maindexlist"
- + ".MainDexListMergeInRootTest$OutsideMainDex"
- + " was not a main dex root in the first round")));
- }));
+ public void testMainDexTracing() throws Exception {
+ testForR8(parameters.getBackend())
+ .addProgramClasses(OutsideMainDex.class, InsideA.class, InsideB.class, Main.class)
+ .addKeepClassAndMembersRules(Main.class)
+ .setMinApi(parameters.getApiLevel())
+ .enableNeverClassInliningAnnotations()
+ .enableNoHorizontalClassMergingAnnotations()
+ .enableInliningAnnotations()
+ .noMinification()
+ .addMainDexRules(
+ "-keep class " + Main.class.getTypeName() + " { public static void main(***); }")
+ .addOptionsModification(
+ options -> {
+ options.testing.checkForNotExpandingMainDexTracingResult = true;
+ })
+ // TODO(b/178151906): See if we can merge the classes.
+ .addHorizontallyMergedClassesInspector(
+ HorizontallyMergedClassesInspector::assertNoClassesMerged)
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutputLines("InsideB::live0", "InsideA::live");
}
@NoHorizontalClassMerging
@@ -80,7 +70,7 @@
public static class InsideA {
public void bar() {
- System.out.println("A::live");
+ System.out.println("InsideA::live");
}
/* Not a traced root */
diff --git a/src/test/java/com/android/tools/r8/maindexlist/MainDexPrunedReferenceTest.java b/src/test/java/com/android/tools/r8/maindexlist/MainDexPrunedReferenceTest.java
index 65bf904..e2d7dd2 100644
--- a/src/test/java/com/android/tools/r8/maindexlist/MainDexPrunedReferenceTest.java
+++ b/src/test/java/com/android/tools/r8/maindexlist/MainDexPrunedReferenceTest.java
@@ -6,8 +6,7 @@
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 static org.junit.Assert.assertEquals;
import static org.junit.Assume.assumeFalse;
import static org.junit.Assume.assumeTrue;
@@ -20,6 +19,7 @@
import com.android.tools.r8.TestParametersCollection;
import com.android.tools.r8.ThrowableConsumer;
import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.google.common.collect.ImmutableSet;
import java.util.Set;
import java.util.function.Consumer;
import org.junit.Assert;
@@ -53,10 +53,7 @@
assumeTrue(parameters.getDexRuntimeVersion().isDalvik());
testMainDex(
builder -> builder.addMainDexListClasses(Main.class),
- mainDexClasses -> {
- assertTrue(mainDexClasses.contains(Main.class.getTypeName()));
- assertFalse(mainDexClasses.contains(Outside.class.getTypeName()));
- });
+ mainDexClasses -> assertEquals(ImmutableSet.of(Main.class.getTypeName()), mainDexClasses));
}
@Test
@@ -66,11 +63,7 @@
builder ->
builder.addMainDexRules(
"-keep class " + Main.class.getTypeName() + " { public static void notMain(); }"),
- mainDexClasses -> {
- assertTrue(mainDexClasses.contains(Main.class.getTypeName()));
- // TODO(b/178362682): This should be false.
- assertTrue(mainDexClasses.contains(Outside.class.getTypeName()));
- });
+ mainDexClasses -> assertEquals(ImmutableSet.of(Main.class.getTypeName()), mainDexClasses));
}
private void testMainDex(
@@ -89,7 +82,7 @@
parameters.getDexRuntimeVersion().isDalvik(),
TestCompilerBuilder::collectMainDexClasses)
.compile()
- .apply(compileResult -> compileResult.inspectMainDexClasses(mainDexListConsumer))
+ .inspectMainDexClasses(mainDexListConsumer)
.run(parameters.getRuntime(), Main.class)
.inspect(
inspector -> {
diff --git a/src/test/java/com/android/tools/r8/maindexlist/MainDexRootIfRuleTest.java b/src/test/java/com/android/tools/r8/maindexlist/MainDexRootIfRuleTest.java
new file mode 100644
index 0000000..4520900
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/maindexlist/MainDexRootIfRuleTest.java
@@ -0,0 +1,81 @@
+// Copyright (c) 2021, 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.maindexlist;
+
+import static org.junit.Assert.assertEquals;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.google.common.collect.ImmutableSet;
+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 MainDexRootIfRuleTest extends TestBase {
+
+ private final TestParameters parameters;
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters()
+ .withDexRuntimes()
+ .withApiLevelsEndingAtExcluding(apiLevelWithNativeMultiDexSupport())
+ .build();
+ }
+
+ public MainDexRootIfRuleTest(TestParameters parameters) {
+ this.parameters = parameters;
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ testForR8(parameters.getBackend())
+ .addInnerClasses(getClass())
+ .setMinApi(parameters.getApiLevel())
+ .addKeepMainRule(Main.class)
+ .addKeepClassRules(R.class)
+ .addMainDexRules(
+ "-keep class " + Main.class.getTypeName() + " { void main(java.lang.String[]); }")
+ .addMainDexRules(
+ "-if class " + MainDexRoot.class.getTypeName() + " { void methodWithSingleCaller(); }",
+ "-keep class " + R.class.getTypeName())
+ .collectMainDexClasses()
+ .compile()
+ .inspectMainDexClasses(
+ mainDexClasses -> {
+ // TODO(b/164019179): Fix if-rules
+ // MainDexRoot will be traced during first round of main dex tracing and result in the
+ // R class being kept due to the if-rule. When tracing in the second round, the
+ // class MainDexRoot is gone and the method inlined, resulting in R not being added to
+ // Main Dex.
+ assertEquals(ImmutableSet.of(Main.class.getTypeName()), mainDexClasses);
+ })
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutputLines(R.class.getName());
+ }
+
+ public static class R {}
+
+ public static class MainDexRoot {
+
+ public static void methodWithSingleCaller() throws Exception {
+ // Reflectively access class R
+ String[] strings =
+ new String[] {"com", "android", "tools", "r8", "maindexlist", "MainDexRootIfRuleTest$R"};
+ Class<?> clazz = Class.forName(String.join(".", strings));
+ System.out.println(clazz.getName());
+ }
+ }
+
+ public static class Main {
+
+ public static void main(String[] args) throws Exception {
+ MainDexRoot.methodWithSingleCaller();
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/maindexlist/MainDexTracingTest.java b/src/test/java/com/android/tools/r8/maindexlist/MainDexTracingTest.java
index ccaa66a..bcea2fe 100644
--- a/src/test/java/com/android/tools/r8/maindexlist/MainDexTracingTest.java
+++ b/src/test/java/com/android/tools/r8/maindexlist/MainDexTracingTest.java
@@ -22,7 +22,6 @@
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestCompilerBuilder;
import com.android.tools.r8.TestParameters;
-import com.android.tools.r8.TestShrinkerBuilder;
import com.android.tools.r8.ThrowableConsumer;
import com.android.tools.r8.ToolHelper;
import com.android.tools.r8.references.Reference;
@@ -212,11 +211,7 @@
Paths.get(EXAMPLE_O_SRC_DIR, "multidex004", "ref-list-1.txt"),
Paths.get(EXAMPLE_O_SRC_DIR, "multidex004", "ref-list-1.txt"),
AndroidApiLevel.I,
- builder ->
- builder
- .applyIf(
- backend.isDex(), TestShrinkerBuilder::addDontWarnCompilerSynthesizedAnnotations)
- .addOptionsModification(options -> options.enableInlining = false));
+ builder -> builder.addOptionsModification(options -> options.enableInlining = false));
}
@Test
diff --git a/src/test/java/com/android/tools/r8/maindexlist/MainDexUnusedArgumentRewriteWithLensTest.java b/src/test/java/com/android/tools/r8/maindexlist/MainDexUnusedArgumentRewriteWithLensTest.java
new file mode 100644
index 0000000..fea8bfa
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/maindexlist/MainDexUnusedArgumentRewriteWithLensTest.java
@@ -0,0 +1,115 @@
+// Copyright (c) 2021, 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.maindexlist;
+
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresentAndNotRenamed;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import com.android.tools.r8.NeverClassInline;
+import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.utils.Box;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.android.tools.r8.utils.codeinspector.MethodSubject;
+import com.google.common.collect.ImmutableSet;
+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 MainDexUnusedArgumentRewriteWithLensTest extends TestBase {
+
+ private final TestParameters parameters;
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters()
+ .withDexRuntimes()
+ .withApiLevelsEndingAtExcluding(apiLevelWithNativeMultiDexSupport())
+ .build();
+ }
+
+ public MainDexUnusedArgumentRewriteWithLensTest(TestParameters parameters) {
+ this.parameters = parameters;
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ Box<Set<String>> mainDex = new Box<>();
+ testForR8(parameters.getBackend())
+ .addInnerClasses(getClass())
+ .enableInliningAnnotations()
+ .enableNeverClassInliningAnnotations()
+ .setMinApi(parameters.getApiLevel())
+ .addKeepClassRules(Dependency.class)
+ .addMainDexRules(
+ "-keep class " + A.class.getTypeName() + " { void foo(java.lang.Object,int); }")
+ .addKeepMainRule(Main.class)
+ .collectMainDexClasses()
+ .compile()
+ .inspectMainDexClasses(mainDex::set)
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutputLines("A::foo")
+ .inspect(
+ inspector -> {
+ ClassSubject aSubject = inspector.clazz(A.class);
+ assertThat(aSubject, isPresent());
+ ClassSubject bSubject = inspector.clazz(B.class);
+ assertThat(bSubject, isPresent());
+ ClassSubject mainSubject = inspector.clazz(Main.class);
+ assertThat(mainSubject, isPresentAndNotRenamed());
+ MethodSubject mainMethodSubject = mainSubject.uniqueMethodWithName("main");
+ assertThat(mainMethodSubject, isPresentAndNotRenamed());
+ assertTrue(
+ mainMethodSubject
+ .streamInstructions()
+ .anyMatch(
+ instructionSubject ->
+ instructionSubject.isNewInstance(Dependency.class.getTypeName())));
+ assertEquals(
+ mainDex.get(),
+ ImmutableSet.of(
+ aSubject.getFinalName(),
+ bSubject.getFinalName(),
+ Dependency.class.getTypeName()));
+ });
+ }
+
+ public static class Dependency {}
+
+ public static class B {
+
+ @NeverInline
+ public static void foo(Object obj) {
+ if (!(obj instanceof Dependency)) {
+ System.out.println("A::foo");
+ }
+ }
+ }
+
+ @NeverClassInline
+ public static class A {
+
+ // Will be rewritten because it has an unused argument
+ @NeverInline
+ public void foo(Object obj, int argumentUnused) {
+ B.foo(obj);
+ }
+ }
+
+ public static class Main {
+
+ public static void main(String[] args) {
+ new A().foo(args.length > 0 ? new Dependency() : new Object(), 42);
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/missingclasses/MissingClassReferencedFromCatchHandlerTest.java b/src/test/java/com/android/tools/r8/missingclasses/MissingClassReferencedFromCatchHandlerTest.java
new file mode 100644
index 0000000..e9bc43b
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/missingclasses/MissingClassReferencedFromCatchHandlerTest.java
@@ -0,0 +1,61 @@
+// 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.missingclasses;
+
+import com.android.tools.r8.CompilationFailedException;
+import com.android.tools.r8.TestDiagnosticMessages;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.references.MethodReference;
+import com.android.tools.r8.references.Reference;
+import com.android.tools.r8.utils.MethodReferenceUtils;
+import org.junit.Test;
+
+public class MissingClassReferencedFromCatchHandlerTest extends MissingClassesTestBase {
+
+ private static final MethodReference referencedFrom =
+ MethodReferenceUtils.mainMethod(Reference.classFromClass(Main.class));
+
+ public MissingClassReferencedFromCatchHandlerTest(TestParameters parameters) {
+ super(parameters);
+ }
+
+ @Test(expected = CompilationFailedException.class)
+ public void testNoRules() throws Exception {
+ compileWithExpectedDiagnostics(
+ Main.class, diagnostics -> inspectDiagnosticsWithNoRules(diagnostics, referencedFrom));
+ }
+
+ @Test
+ public void testDontWarnMainClass() throws Exception {
+ compileWithExpectedDiagnostics(
+ Main.class, TestDiagnosticMessages::assertNoMessages, addDontWarn(Main.class));
+ }
+
+ @Test
+ public void testDontWarnMissingClass() throws Exception {
+ compileWithExpectedDiagnostics(
+ Main.class, TestDiagnosticMessages::assertNoMessages, addDontWarn(MissingClass.class));
+ }
+
+ @Test
+ public void testIgnoreWarnings() throws Exception {
+ compileWithExpectedDiagnostics(
+ Main.class,
+ diagnostics -> inspectDiagnosticsWithIgnoreWarnings(diagnostics, referencedFrom),
+ addIgnoreWarnings());
+ }
+
+ static class Main {
+
+ public static void main(String[] args) {
+ try {
+ nop();
+ } catch (MissingClass ignore) {
+ }
+ }
+
+ private static void nop() {}
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/missingclasses/MissingClassReferencedFromCheckCastTest.java b/src/test/java/com/android/tools/r8/missingclasses/MissingClassReferencedFromCheckCastTest.java
new file mode 100644
index 0000000..b10a231
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/missingclasses/MissingClassReferencedFromCheckCastTest.java
@@ -0,0 +1,56 @@
+// 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.missingclasses;
+
+import com.android.tools.r8.CompilationFailedException;
+import com.android.tools.r8.TestDiagnosticMessages;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.references.MethodReference;
+import com.android.tools.r8.references.Reference;
+import com.android.tools.r8.utils.MethodReferenceUtils;
+import org.junit.Test;
+
+public class MissingClassReferencedFromCheckCastTest extends MissingClassesTestBase {
+
+ private static final MethodReference referencedFrom =
+ MethodReferenceUtils.mainMethod(Reference.classFromClass(Main.class));
+
+ public MissingClassReferencedFromCheckCastTest(TestParameters parameters) {
+ super(parameters);
+ }
+
+ @Test(expected = CompilationFailedException.class)
+ public void testNoRules() throws Exception {
+ compileWithExpectedDiagnostics(
+ Main.class, diagnostics -> inspectDiagnosticsWithNoRules(diagnostics, referencedFrom));
+ }
+
+ @Test
+ public void testDontWarnMainClass() throws Exception {
+ compileWithExpectedDiagnostics(
+ Main.class, TestDiagnosticMessages::assertNoMessages, addDontWarn(Main.class));
+ }
+
+ @Test
+ public void testDontWarnMissingClass() throws Exception {
+ compileWithExpectedDiagnostics(
+ Main.class, TestDiagnosticMessages::assertNoMessages, addDontWarn(MissingClass.class));
+ }
+
+ @Test
+ public void testIgnoreWarnings() throws Exception {
+ compileWithExpectedDiagnostics(
+ Main.class,
+ diagnostics -> inspectDiagnosticsWithIgnoreWarnings(diagnostics, referencedFrom),
+ addIgnoreWarnings());
+ }
+
+ static class Main {
+
+ public static void main(String[] args) {
+ MissingClass ignore = (MissingClass) null;
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/missingclasses/MissingClassReferencedFromConstClassTest.java b/src/test/java/com/android/tools/r8/missingclasses/MissingClassReferencedFromConstClassTest.java
new file mode 100644
index 0000000..cb6287c
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/missingclasses/MissingClassReferencedFromConstClassTest.java
@@ -0,0 +1,56 @@
+// 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.missingclasses;
+
+import com.android.tools.r8.CompilationFailedException;
+import com.android.tools.r8.TestDiagnosticMessages;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.references.MethodReference;
+import com.android.tools.r8.references.Reference;
+import com.android.tools.r8.utils.MethodReferenceUtils;
+import org.junit.Test;
+
+public class MissingClassReferencedFromConstClassTest extends MissingClassesTestBase {
+
+ private static final MethodReference referencedFrom =
+ MethodReferenceUtils.mainMethod(Reference.classFromClass(Main.class));
+
+ public MissingClassReferencedFromConstClassTest(TestParameters parameters) {
+ super(parameters);
+ }
+
+ @Test(expected = CompilationFailedException.class)
+ public void testNoRules() throws Exception {
+ compileWithExpectedDiagnostics(
+ Main.class, diagnostics -> inspectDiagnosticsWithNoRules(diagnostics, referencedFrom));
+ }
+
+ @Test
+ public void testDontWarnMainClass() throws Exception {
+ compileWithExpectedDiagnostics(
+ Main.class, TestDiagnosticMessages::assertNoMessages, addDontWarn(Main.class));
+ }
+
+ @Test
+ public void testDontWarnMissingClass() throws Exception {
+ compileWithExpectedDiagnostics(
+ Main.class, TestDiagnosticMessages::assertNoMessages, addDontWarn(MissingClass.class));
+ }
+
+ @Test
+ public void testIgnoreWarnings() throws Exception {
+ compileWithExpectedDiagnostics(
+ Main.class,
+ diagnostics -> inspectDiagnosticsWithIgnoreWarnings(diagnostics, referencedFrom),
+ addIgnoreWarnings());
+ }
+
+ static class Main {
+
+ public static void main(String[] args) {
+ Class<?> ignore = MissingClass.class;
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/missingclasses/MissingClassReferencedFromEnclosingMethodAttributeTest.java b/src/test/java/com/android/tools/r8/missingclasses/MissingClassReferencedFromEnclosingMethodAttributeTest.java
new file mode 100644
index 0000000..74f00d4
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/missingclasses/MissingClassReferencedFromEnclosingMethodAttributeTest.java
@@ -0,0 +1,111 @@
+// 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.missingclasses;
+
+import static com.android.tools.r8.utils.codeinspector.AssertUtils.assertFailsCompilationIf;
+import static org.junit.Assert.assertFalse;
+
+import com.android.tools.r8.R8FullTestBuilder;
+import com.android.tools.r8.R8TestBuilder;
+import com.android.tools.r8.TestDiagnosticMessages;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.references.ClassReference;
+import com.android.tools.r8.references.Reference;
+import com.android.tools.r8.utils.BooleanUtils;
+import java.util.List;
+import org.junit.Test;
+import org.junit.runners.Parameterized.Parameters;
+
+public class MissingClassReferencedFromEnclosingMethodAttributeTest extends MissingClassesTestBase {
+
+ private static final ClassReference referencedFrom = Reference.classFromClass(getMainClass());
+
+ @Parameters(name = "{1}, report: {0}")
+ public static List<Object[]> data() {
+ return buildParameters(
+ BooleanUtils.values(), getTestParameters().withAllRuntimesAndApiLevels().build());
+ }
+
+ private final boolean reportMissingClassesInEnclosingMethodAttribute;
+
+ public MissingClassReferencedFromEnclosingMethodAttributeTest(
+ boolean reportMissingClassesInEnclosingMethodAttribute, TestParameters parameters) {
+ super(parameters);
+ this.reportMissingClassesInEnclosingMethodAttribute =
+ reportMissingClassesInEnclosingMethodAttribute;
+ }
+
+ @Test
+ public void testNoRules() throws Exception {
+ assertFailsCompilationIf(
+ reportMissingClassesInEnclosingMethodAttribute,
+ () ->
+ compileWithExpectedDiagnostics(
+ getMainClass(),
+ reportMissingClassesInEnclosingMethodAttribute
+ ? diagnostics -> inspectDiagnosticsWithNoRules(diagnostics, referencedFrom)
+ : TestDiagnosticMessages::assertNoMessages,
+ this::configure));
+ }
+
+ @Test
+ public void testDontWarnMainClass() throws Exception {
+ compileWithExpectedDiagnostics(
+ getMainClass(),
+ TestDiagnosticMessages::assertNoMessages,
+ addDontWarn(getMainClass()).andThen(this::configure));
+ }
+
+ @Test
+ public void testDontWarnMissingClass() throws Exception {
+ compileWithExpectedDiagnostics(
+ getMainClass(),
+ TestDiagnosticMessages::assertNoMessages,
+ addDontWarn(MissingClass.class).andThen(this::configure));
+ }
+
+ @Test
+ public void testIgnoreWarnings() throws Exception {
+ compileWithExpectedDiagnostics(
+ getMainClass(),
+ reportMissingClassesInEnclosingMethodAttribute
+ ? diagnostics -> inspectDiagnosticsWithIgnoreWarnings(diagnostics, referencedFrom)
+ : TestDiagnosticMessages::assertNoMessages,
+ addIgnoreWarnings(reportMissingClassesInEnclosingMethodAttribute).andThen(this::configure));
+ }
+
+ void configure(R8FullTestBuilder builder) {
+ builder
+ .addKeepAttributeInnerClassesAndEnclosingMethod()
+ .addOptionsModification(
+ options -> {
+ // We do not report missing classes from inner class attributes by default.
+ assertFalse(options.reportMissingClassesInEnclosingMethodAttribute);
+ options.reportMissingClassesInEnclosingMethodAttribute =
+ reportMissingClassesInEnclosingMethodAttribute;
+ })
+ .applyIf(
+ !reportMissingClassesInEnclosingMethodAttribute,
+ // The -dontwarn Main and -dontwarn MissingClass tests will have unused -dontwarn rules.
+ R8TestBuilder::allowUnusedDontWarnPatterns);
+ }
+
+ static Class<?> getMainClass() {
+ return MissingClass.getMainClass();
+ }
+
+ @Override
+ ClassReference getMissingClassReference() {
+ return Reference.classFromClass(MissingClass.class);
+ }
+
+ static class MissingClass {
+
+ static Class<?> getMainClass() {
+ class Main {}
+ return Main.class;
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/missingclasses/MissingClassReferencedFromImplementsClauseTest.java b/src/test/java/com/android/tools/r8/missingclasses/MissingClassReferencedFromImplementsClauseTest.java
new file mode 100644
index 0000000..490efdc
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/missingclasses/MissingClassReferencedFromImplementsClauseTest.java
@@ -0,0 +1,67 @@
+// 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.missingclasses;
+
+import com.android.tools.r8.CompilationFailedException;
+import com.android.tools.r8.TestCompilerBuilder;
+import com.android.tools.r8.TestDiagnosticMessages;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.references.ClassReference;
+import com.android.tools.r8.references.Reference;
+import org.junit.Test;
+
+public class MissingClassReferencedFromImplementsClauseTest extends MissingClassesTestBase {
+
+ private static final ClassReference referencedFrom = Reference.classFromClass(Main.class);
+
+ public MissingClassReferencedFromImplementsClauseTest(TestParameters parameters) {
+ super(parameters);
+ }
+
+ @Override
+ ClassReference getMissingClassReference() {
+ return Reference.classFromClass(MissingInterface.class);
+ }
+
+ // The tests explicitly disable desugaring to prevent desugaring warnings.
+ // TODO(b/179341237): Consider if desugaring warnings should be D8 only, since in a way they are
+ // all duplicates of the MissingClassesDiagnostic.
+ @Test(expected = CompilationFailedException.class)
+ public void testNoRules() throws Exception {
+ compileWithExpectedDiagnostics(
+ Main.class,
+ diagnostics -> inspectDiagnosticsWithNoRules(diagnostics, referencedFrom),
+ TestCompilerBuilder::disableDesugaring);
+ }
+
+ @Test
+ public void testDontWarnMainClass() throws Exception {
+ compileWithExpectedDiagnostics(
+ Main.class,
+ TestDiagnosticMessages::assertNoMessages,
+ addDontWarn(Main.class).andThen(TestCompilerBuilder::disableDesugaring));
+ }
+
+ @Test
+ public void testDontWarnMissingClass() throws Exception {
+ compileWithExpectedDiagnostics(
+ Main.class,
+ TestDiagnosticMessages::assertNoMessages,
+ addDontWarn(MissingInterface.class).andThen(TestCompilerBuilder::disableDesugaring));
+ }
+
+ @Test
+ public void testIgnoreWarnings() throws Exception {
+ compileWithExpectedDiagnostics(
+ Main.class,
+ diagnostics -> inspectDiagnosticsWithIgnoreWarnings(diagnostics, referencedFrom),
+ addIgnoreWarnings().andThen(TestCompilerBuilder::disableDesugaring));
+ }
+
+ static class Main implements MissingInterface {
+
+ public static void main(String[] args) {}
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/missingclasses/MissingClassReferencedFromInnerClassAttributeTest.java b/src/test/java/com/android/tools/r8/missingclasses/MissingClassReferencedFromInnerClassAttributeTest.java
new file mode 100644
index 0000000..aa64ab8
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/missingclasses/MissingClassReferencedFromInnerClassAttributeTest.java
@@ -0,0 +1,107 @@
+// 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.missingclasses;
+
+import static com.android.tools.r8.utils.codeinspector.AssertUtils.assertFailsCompilationIf;
+import static org.junit.Assert.assertFalse;
+
+import com.android.tools.r8.R8FullTestBuilder;
+import com.android.tools.r8.R8TestBuilder;
+import com.android.tools.r8.TestDiagnosticMessages;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.references.ClassReference;
+import com.android.tools.r8.references.Reference;
+import com.android.tools.r8.utils.BooleanUtils;
+import java.util.List;
+import org.junit.Test;
+import org.junit.runners.Parameterized.Parameters;
+
+public class MissingClassReferencedFromInnerClassAttributeTest extends MissingClassesTestBase {
+
+ private static final ClassReference referencedFrom = Reference.classFromClass(Main.class);
+
+ @Parameters(name = "{1}, report: {0}")
+ public static List<Object[]> data() {
+ return buildParameters(
+ BooleanUtils.values(), getTestParameters().withAllRuntimesAndApiLevels().build());
+ }
+
+ private final boolean reportMissingClassesInInnerClassAttributes;
+
+ public MissingClassReferencedFromInnerClassAttributeTest(
+ boolean reportMissingClassesInInnerClassAttributes, TestParameters parameters) {
+ super(parameters);
+ this.reportMissingClassesInInnerClassAttributes = reportMissingClassesInInnerClassAttributes;
+ }
+
+ @Test
+ public void testNoRules() throws Exception {
+ assertFailsCompilationIf(
+ reportMissingClassesInInnerClassAttributes,
+ () ->
+ compileWithExpectedDiagnostics(
+ Main.class,
+ reportMissingClassesInInnerClassAttributes
+ ? diagnostics -> inspectDiagnosticsWithNoRules(diagnostics, referencedFrom)
+ : TestDiagnosticMessages::assertNoMessages,
+ this::configure));
+ }
+
+ @Test
+ public void testDontWarnMainClass() throws Exception {
+ compileWithExpectedDiagnostics(
+ Main.class,
+ TestDiagnosticMessages::assertNoMessages,
+ addDontWarn(Main.class).andThen(this::configure));
+ }
+
+ @Test
+ public void testDontWarnMissingClass() throws Exception {
+ compileWithExpectedDiagnostics(
+ Main.class,
+ TestDiagnosticMessages::assertNoMessages,
+ addDontWarn(Main.MissingClass.class).andThen(this::configure));
+ }
+
+ @Test
+ public void testIgnoreWarnings() throws Exception {
+ compileWithExpectedDiagnostics(
+ Main.class,
+ reportMissingClassesInInnerClassAttributes
+ ? diagnostics -> inspectDiagnosticsWithIgnoreWarnings(diagnostics, referencedFrom)
+ : TestDiagnosticMessages::assertNoMessages,
+ addIgnoreWarnings(reportMissingClassesInInnerClassAttributes).andThen(this::configure));
+ }
+
+ void configure(R8FullTestBuilder builder) {
+ builder
+ .addKeepAttributeInnerClassesAndEnclosingMethod()
+ .addOptionsModification(
+ options -> {
+ // We do not report missing classes from inner class attributes by default.
+ assertFalse(options.reportMissingClassesInInnerClassAttributes);
+ options.reportMissingClassesInInnerClassAttributes =
+ reportMissingClassesInInnerClassAttributes;
+ })
+ .applyIf(
+ reportMissingClassesInInnerClassAttributes,
+ // We need to ignore the inner class attribute for the test class.
+ addDontWarn(MissingClassReferencedFromInnerClassAttributeTest.class),
+ // The -dontwarn Main and -dontwarn MissingClass tests will have unused -dontwarn rules.
+ R8TestBuilder::allowUnusedDontWarnPatterns);
+ }
+
+ @Override
+ ClassReference getMissingClassReference() {
+ return Reference.classFromClass(Main.MissingClass.class);
+ }
+
+ static class Main {
+
+ public static void main(String[] args) {}
+
+ static class MissingClass {}
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/missingclasses/MissingClassReferencedFromInstanceGetToExistingFieldTest.java b/src/test/java/com/android/tools/r8/missingclasses/MissingClassReferencedFromInstanceGetToExistingFieldTest.java
new file mode 100644
index 0000000..6c7f620
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/missingclasses/MissingClassReferencedFromInstanceGetToExistingFieldTest.java
@@ -0,0 +1,65 @@
+// 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.missingclasses;
+
+import com.android.tools.r8.CompilationFailedException;
+import com.android.tools.r8.TestDiagnosticMessages;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.references.FieldReference;
+import com.android.tools.r8.references.Reference;
+import org.junit.Test;
+
+/**
+ * If a field reference that refers to a missing class resolves to a definition, then the field
+ * definition is to be blamed.
+ */
+public class MissingClassReferencedFromInstanceGetToExistingFieldTest
+ extends MissingClassesTestBase {
+
+ private static final FieldReference referencedFrom =
+ Reference.field(
+ Reference.classFromClass(Main.class),
+ "field",
+ Reference.classFromClass(MissingClass.class));
+
+ public MissingClassReferencedFromInstanceGetToExistingFieldTest(TestParameters parameters) {
+ super(parameters);
+ }
+
+ @Test(expected = CompilationFailedException.class)
+ public void testNoRules() throws Exception {
+ compileWithExpectedDiagnostics(
+ Main.class, diagnostics -> inspectDiagnosticsWithNoRules(diagnostics, referencedFrom));
+ }
+
+ @Test
+ public void testDontWarnMainClass() throws Exception {
+ compileWithExpectedDiagnostics(
+ Main.class, TestDiagnosticMessages::assertNoMessages, addDontWarn(Main.class));
+ }
+
+ @Test
+ public void testDontWarnMissingClass() throws Exception {
+ compileWithExpectedDiagnostics(
+ Main.class, TestDiagnosticMessages::assertNoMessages, addDontWarn(MissingClass.class));
+ }
+
+ @Test
+ public void testIgnoreWarnings() throws Exception {
+ compileWithExpectedDiagnostics(
+ Main.class,
+ diagnostics -> inspectDiagnosticsWithIgnoreWarnings(diagnostics, referencedFrom),
+ addIgnoreWarnings());
+ }
+
+ static class Main {
+
+ public MissingClass field;
+
+ public static void main(String[] args) {
+ MissingClass ignore = new Main().field;
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/missingclasses/MissingClassReferencedFromInstanceGetToMissingFieldTest.java b/src/test/java/com/android/tools/r8/missingclasses/MissingClassReferencedFromInstanceGetToMissingFieldTest.java
new file mode 100644
index 0000000..851d65d
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/missingclasses/MissingClassReferencedFromInstanceGetToMissingFieldTest.java
@@ -0,0 +1,61 @@
+// 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.missingclasses;
+
+import com.android.tools.r8.CompilationFailedException;
+import com.android.tools.r8.TestDiagnosticMessages;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.references.MethodReference;
+import com.android.tools.r8.references.Reference;
+import com.android.tools.r8.utils.MethodReferenceUtils;
+import org.junit.Test;
+
+/**
+ * If a field reference that refers to a missing class does not resolve, then the enclosing method
+ * is to be blamed.
+ */
+public class MissingClassReferencedFromInstanceGetToMissingFieldTest
+ extends MissingClassesTestBase {
+
+ private static final MethodReference referencedFrom =
+ MethodReferenceUtils.mainMethod(Reference.classFromClass(Main.class));
+
+ public MissingClassReferencedFromInstanceGetToMissingFieldTest(TestParameters parameters) {
+ super(parameters);
+ }
+
+ @Test(expected = CompilationFailedException.class)
+ public void testNoRules() throws Exception {
+ compileWithExpectedDiagnostics(
+ Main.class, diagnostics -> inspectDiagnosticsWithNoRules(diagnostics, referencedFrom));
+ }
+
+ @Test
+ public void testDontWarnMainClass() throws Exception {
+ compileWithExpectedDiagnostics(
+ Main.class, TestDiagnosticMessages::assertNoMessages, addDontWarn(Main.class));
+ }
+
+ @Test
+ public void testDontWarnMissingClass() throws Exception {
+ compileWithExpectedDiagnostics(
+ Main.class, TestDiagnosticMessages::assertNoMessages, addDontWarn(MissingClass.class));
+ }
+
+ @Test
+ public void testIgnoreWarnings() throws Exception {
+ compileWithExpectedDiagnostics(
+ Main.class,
+ diagnostics -> inspectDiagnosticsWithIgnoreWarnings(diagnostics, referencedFrom),
+ addIgnoreWarnings());
+ }
+
+ static class Main {
+
+ public static void main(String[] args) {
+ int ignore = new MissingClass().field;
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/missingclasses/MissingClassReferencedFromInstanceOfTest.java b/src/test/java/com/android/tools/r8/missingclasses/MissingClassReferencedFromInstanceOfTest.java
new file mode 100644
index 0000000..2d05aac
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/missingclasses/MissingClassReferencedFromInstanceOfTest.java
@@ -0,0 +1,56 @@
+// 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.missingclasses;
+
+import com.android.tools.r8.CompilationFailedException;
+import com.android.tools.r8.TestDiagnosticMessages;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.references.MethodReference;
+import com.android.tools.r8.references.Reference;
+import com.android.tools.r8.utils.MethodReferenceUtils;
+import org.junit.Test;
+
+public class MissingClassReferencedFromInstanceOfTest extends MissingClassesTestBase {
+
+ private static final MethodReference referencedFrom =
+ MethodReferenceUtils.mainMethod(Reference.classFromClass(Main.class));
+
+ public MissingClassReferencedFromInstanceOfTest(TestParameters parameters) {
+ super(parameters);
+ }
+
+ @Test(expected = CompilationFailedException.class)
+ public void testNoRules() throws Exception {
+ compileWithExpectedDiagnostics(
+ Main.class, diagnostics -> inspectDiagnosticsWithNoRules(diagnostics, referencedFrom));
+ }
+
+ @Test
+ public void testDontWarnMainClass() throws Exception {
+ compileWithExpectedDiagnostics(
+ Main.class, TestDiagnosticMessages::assertNoMessages, addDontWarn(Main.class));
+ }
+
+ @Test
+ public void testDontWarnMissingClass() throws Exception {
+ compileWithExpectedDiagnostics(
+ Main.class, TestDiagnosticMessages::assertNoMessages, addDontWarn(MissingClass.class));
+ }
+
+ @Test
+ public void testIgnoreWarnings() throws Exception {
+ compileWithExpectedDiagnostics(
+ Main.class,
+ diagnostics -> inspectDiagnosticsWithIgnoreWarnings(diagnostics, referencedFrom),
+ addIgnoreWarnings());
+ }
+
+ static class Main {
+
+ public static void main(String[] args) {
+ boolean ignore = null instanceof MissingClass;
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/missingclasses/MissingClassReferencedFromInstancePutToExistingFieldTest.java b/src/test/java/com/android/tools/r8/missingclasses/MissingClassReferencedFromInstancePutToExistingFieldTest.java
new file mode 100644
index 0000000..b69625e
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/missingclasses/MissingClassReferencedFromInstancePutToExistingFieldTest.java
@@ -0,0 +1,65 @@
+// 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.missingclasses;
+
+import com.android.tools.r8.CompilationFailedException;
+import com.android.tools.r8.TestDiagnosticMessages;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.references.FieldReference;
+import com.android.tools.r8.references.Reference;
+import org.junit.Test;
+
+/**
+ * If a field reference that refers to a missing class resolves to a definition, then the field
+ * definition is to be blamed.
+ */
+public class MissingClassReferencedFromInstancePutToExistingFieldTest
+ extends MissingClassesTestBase {
+
+ private static final FieldReference referencedFrom =
+ Reference.field(
+ Reference.classFromClass(Main.class),
+ "field",
+ Reference.classFromClass(MissingClass.class));
+
+ public MissingClassReferencedFromInstancePutToExistingFieldTest(TestParameters parameters) {
+ super(parameters);
+ }
+
+ @Test(expected = CompilationFailedException.class)
+ public void testNoRules() throws Exception {
+ compileWithExpectedDiagnostics(
+ Main.class, diagnostics -> inspectDiagnosticsWithNoRules(diagnostics, referencedFrom));
+ }
+
+ @Test
+ public void testDontWarnMainClass() throws Exception {
+ compileWithExpectedDiagnostics(
+ Main.class, TestDiagnosticMessages::assertNoMessages, addDontWarn(Main.class));
+ }
+
+ @Test
+ public void testDontWarnMissingClass() throws Exception {
+ compileWithExpectedDiagnostics(
+ Main.class, TestDiagnosticMessages::assertNoMessages, addDontWarn(MissingClass.class));
+ }
+
+ @Test
+ public void testIgnoreWarnings() throws Exception {
+ compileWithExpectedDiagnostics(
+ Main.class,
+ diagnostics -> inspectDiagnosticsWithIgnoreWarnings(diagnostics, referencedFrom),
+ addIgnoreWarnings());
+ }
+
+ static class Main {
+
+ MissingClass field;
+
+ public static void main(String[] args) {
+ new Main().field = null;
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/missingclasses/MissingClassReferencedFromInstancePutToMissingFieldTest.java b/src/test/java/com/android/tools/r8/missingclasses/MissingClassReferencedFromInstancePutToMissingFieldTest.java
new file mode 100644
index 0000000..ec615f1
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/missingclasses/MissingClassReferencedFromInstancePutToMissingFieldTest.java
@@ -0,0 +1,61 @@
+// 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.missingclasses;
+
+import com.android.tools.r8.CompilationFailedException;
+import com.android.tools.r8.TestDiagnosticMessages;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.references.MethodReference;
+import com.android.tools.r8.references.Reference;
+import com.android.tools.r8.utils.MethodReferenceUtils;
+import org.junit.Test;
+
+/**
+ * If a field reference that refers to a missing class does not resolve, then the enclosing method
+ * is to be blamed.
+ */
+public class MissingClassReferencedFromInstancePutToMissingFieldTest
+ extends MissingClassesTestBase {
+
+ private static final MethodReference referencedFrom =
+ MethodReferenceUtils.mainMethod(Reference.classFromClass(Main.class));
+
+ public MissingClassReferencedFromInstancePutToMissingFieldTest(TestParameters parameters) {
+ super(parameters);
+ }
+
+ @Test(expected = CompilationFailedException.class)
+ public void testNoRules() throws Exception {
+ compileWithExpectedDiagnostics(
+ Main.class, diagnostics -> inspectDiagnosticsWithNoRules(diagnostics, referencedFrom));
+ }
+
+ @Test
+ public void testDontWarnMainClass() throws Exception {
+ compileWithExpectedDiagnostics(
+ Main.class, TestDiagnosticMessages::assertNoMessages, addDontWarn(Main.class));
+ }
+
+ @Test
+ public void testDontWarnMissingClass() throws Exception {
+ compileWithExpectedDiagnostics(
+ Main.class, TestDiagnosticMessages::assertNoMessages, addDontWarn(MissingClass.class));
+ }
+
+ @Test
+ public void testIgnoreWarnings() throws Exception {
+ compileWithExpectedDiagnostics(
+ Main.class,
+ diagnostics -> inspectDiagnosticsWithIgnoreWarnings(diagnostics, referencedFrom),
+ addIgnoreWarnings());
+ }
+
+ static class Main {
+
+ public static void main(String[] args) {
+ new MissingClass().field = 42;
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/missingclasses/MissingClassReferencedFromKeptFieldTest.java b/src/test/java/com/android/tools/r8/missingclasses/MissingClassReferencedFromKeptFieldTest.java
new file mode 100644
index 0000000..19dfdea
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/missingclasses/MissingClassReferencedFromKeptFieldTest.java
@@ -0,0 +1,73 @@
+// 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.missingclasses;
+
+import com.android.tools.r8.CompilationFailedException;
+import com.android.tools.r8.R8FullTestBuilder;
+import com.android.tools.r8.TestDiagnosticMessages;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.references.FieldReference;
+import com.android.tools.r8.references.Reference;
+import org.junit.Test;
+
+/** If a field definition refers to a missing class, then the field definition is to be blamed. */
+public class MissingClassReferencedFromKeptFieldTest extends MissingClassesTestBase {
+
+ private static final FieldReference referencedFrom =
+ Reference.field(
+ Reference.classFromClass(Main.class),
+ "FIELD",
+ Reference.classFromClass(MissingClass.class));
+
+ public MissingClassReferencedFromKeptFieldTest(TestParameters parameters) {
+ super(parameters);
+ }
+
+ @Test(expected = CompilationFailedException.class)
+ public void testNoRules() throws Exception {
+ compileWithExpectedDiagnostics(
+ Main.class,
+ diagnostics -> inspectDiagnosticsWithNoRules(diagnostics, referencedFrom),
+ this::addKeepFieldRule);
+ }
+
+ @Test
+ public void testDontWarnMainClass() throws Exception {
+ compileWithExpectedDiagnostics(
+ Main.class,
+ TestDiagnosticMessages::assertNoMessages,
+ addDontWarn(Main.class).andThen(this::addKeepFieldRule));
+ }
+
+ @Test
+ public void testDontWarnMissingClass() throws Exception {
+ compileWithExpectedDiagnostics(
+ Main.class,
+ TestDiagnosticMessages::assertNoMessages,
+ addDontWarn(MissingClass.class).andThen(this::addKeepFieldRule));
+ }
+
+ @Test
+ public void testIgnoreWarnings() throws Exception {
+ compileWithExpectedDiagnostics(
+ Main.class,
+ diagnostics -> inspectDiagnosticsWithIgnoreWarnings(diagnostics, referencedFrom),
+ addIgnoreWarnings().andThen(this::addKeepFieldRule));
+ }
+
+ private void addKeepFieldRule(R8FullTestBuilder builder) {
+ builder.addKeepRules(
+ "-keep class " + Main.class.getTypeName() + " {",
+ " public static " + MissingClass.class.getTypeName() + " FIELD;",
+ "}");
+ }
+
+ static class Main {
+
+ public static MissingClass FIELD;
+
+ public static void main(String[] args) {}
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/missingclasses/MissingClassReferencedFromNestHostAttributeTest.java b/src/test/java/com/android/tools/r8/missingclasses/MissingClassReferencedFromNestHostAttributeTest.java
new file mode 100644
index 0000000..fc1ac99
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/missingclasses/MissingClassReferencedFromNestHostAttributeTest.java
@@ -0,0 +1,76 @@
+// 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.missingclasses;
+
+import com.android.tools.r8.CompilationFailedException;
+import com.android.tools.r8.R8FullTestBuilder;
+import com.android.tools.r8.TestDiagnosticMessages;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.ThrowableConsumer;
+import com.android.tools.r8.missingclasses.MissingClassReferencedFromNestHostAttributeTest.MissingClass.Main;
+import com.android.tools.r8.references.ClassReference;
+import com.android.tools.r8.references.Reference;
+import com.google.common.collect.ImmutableList;
+import java.io.IOException;
+import java.util.Collection;
+import org.junit.Test;
+
+public class MissingClassReferencedFromNestHostAttributeTest extends MissingClassesTestBase {
+
+ private static final ClassReference referencedFrom = Reference.classFromClass(Main.class);
+
+ public MissingClassReferencedFromNestHostAttributeTest(TestParameters parameters) {
+ super(parameters);
+ }
+
+ @Test(expected = CompilationFailedException.class)
+ public void testNoRules() throws Exception {
+ compileWithExpectedDiagnostics(
+ addMain(), diagnostics -> inspectDiagnosticsWithNoRules(diagnostics, referencedFrom));
+ }
+
+ @Test
+ public void testDontWarnMainClass() throws Exception {
+ compileWithExpectedDiagnostics(
+ addMain().andThen(addDontWarn(Main.class)), TestDiagnosticMessages::assertNoMessages);
+ }
+
+ @Test
+ public void testDontWarnMissingClass() throws Exception {
+ compileWithExpectedDiagnostics(
+ addMain().andThen(addDontWarn(MissingClass.class)),
+ TestDiagnosticMessages::assertNoMessages);
+ }
+
+ @Test
+ public void testIgnoreWarnings() throws Exception {
+ compileWithExpectedDiagnostics(
+ addMain().andThen(addIgnoreWarnings()),
+ diagnostics -> inspectDiagnosticsWithIgnoreWarnings(diagnostics, referencedFrom));
+ }
+
+ private ThrowableConsumer<R8FullTestBuilder> addMain() {
+ return builder ->
+ builder.addProgramClassFileData(getProgramClassFileData()).addKeepMainRule(Main.class);
+ }
+
+ @Override
+ ClassReference getMissingClassReference() {
+ return Reference.classFromClass(MissingClass.class);
+ }
+
+ static Collection<byte[]> getProgramClassFileData() throws IOException {
+ return ImmutableList.of(
+ transformer(Main.class).setNest(MissingClass.class, Main.class).transform());
+ }
+
+ static /*host*/ class MissingClass {
+
+ static /*member*/ class Main {
+
+ public static void main(String[] args) {}
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/missingclasses/MissingClassReferencedFromNestMemberAttributeTest.java b/src/test/java/com/android/tools/r8/missingclasses/MissingClassReferencedFromNestMemberAttributeTest.java
new file mode 100644
index 0000000..a151dce
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/missingclasses/MissingClassReferencedFromNestMemberAttributeTest.java
@@ -0,0 +1,74 @@
+// 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.missingclasses;
+
+import com.android.tools.r8.CompilationFailedException;
+import com.android.tools.r8.R8FullTestBuilder;
+import com.android.tools.r8.TestDiagnosticMessages;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.ThrowableConsumer;
+import com.android.tools.r8.references.ClassReference;
+import com.android.tools.r8.references.Reference;
+import com.google.common.collect.ImmutableList;
+import java.io.IOException;
+import java.util.Collection;
+import org.junit.Test;
+
+public class MissingClassReferencedFromNestMemberAttributeTest extends MissingClassesTestBase {
+
+ private static final ClassReference referencedFrom = Reference.classFromClass(Main.class);
+
+ public MissingClassReferencedFromNestMemberAttributeTest(TestParameters parameters) {
+ super(parameters);
+ }
+
+ @Test(expected = CompilationFailedException.class)
+ public void testNoRules() throws Exception {
+ compileWithExpectedDiagnostics(
+ addMain(), diagnostics -> inspectDiagnosticsWithNoRules(diagnostics, referencedFrom));
+ }
+
+ @Test
+ public void testDontWarnMainClass() throws Exception {
+ compileWithExpectedDiagnostics(
+ addMain().andThen(addDontWarn(Main.class)), TestDiagnosticMessages::assertNoMessages);
+ }
+
+ @Test
+ public void testDontWarnMissingClass() throws Exception {
+ compileWithExpectedDiagnostics(
+ addMain().andThen(addDontWarn(Main.MissingClass.class)),
+ TestDiagnosticMessages::assertNoMessages);
+ }
+
+ @Test
+ public void testIgnoreWarnings() throws Exception {
+ compileWithExpectedDiagnostics(
+ addMain().andThen(addIgnoreWarnings()),
+ diagnostics -> inspectDiagnosticsWithIgnoreWarnings(diagnostics, referencedFrom));
+ }
+
+ private ThrowableConsumer<R8FullTestBuilder> addMain() {
+ return builder ->
+ builder.addProgramClassFileData(getProgramClassFileData()).addKeepMainRule(Main.class);
+ }
+
+ @Override
+ ClassReference getMissingClassReference() {
+ return Reference.classFromClass(Main.MissingClass.class);
+ }
+
+ static Collection<byte[]> getProgramClassFileData() throws IOException {
+ return ImmutableList.of(
+ transformer(Main.class).setNest(Main.class, Main.MissingClass.class).transform());
+ }
+
+ static /*host*/ class Main {
+
+ static /*member*/ class MissingClass {}
+
+ public static void main(String[] args) {}
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/missingclasses/MissingClassReferencedFromNewArrayTest.java b/src/test/java/com/android/tools/r8/missingclasses/MissingClassReferencedFromNewArrayTest.java
new file mode 100644
index 0000000..66242bf
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/missingclasses/MissingClassReferencedFromNewArrayTest.java
@@ -0,0 +1,56 @@
+// 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.missingclasses;
+
+import com.android.tools.r8.CompilationFailedException;
+import com.android.tools.r8.TestDiagnosticMessages;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.references.MethodReference;
+import com.android.tools.r8.references.Reference;
+import com.android.tools.r8.utils.MethodReferenceUtils;
+import org.junit.Test;
+
+public class MissingClassReferencedFromNewArrayTest extends MissingClassesTestBase {
+
+ private static final MethodReference referencedFrom =
+ MethodReferenceUtils.mainMethod(Reference.classFromClass(Main.class));
+
+ public MissingClassReferencedFromNewArrayTest(TestParameters parameters) {
+ super(parameters);
+ }
+
+ @Test(expected = CompilationFailedException.class)
+ public void testNoRules() throws Exception {
+ compileWithExpectedDiagnostics(
+ Main.class, diagnostics -> inspectDiagnosticsWithNoRules(diagnostics, referencedFrom));
+ }
+
+ @Test
+ public void testDontWarnMainClass() throws Exception {
+ compileWithExpectedDiagnostics(
+ Main.class, TestDiagnosticMessages::assertNoMessages, addDontWarn(Main.class));
+ }
+
+ @Test
+ public void testDontWarnMissingClass() throws Exception {
+ compileWithExpectedDiagnostics(
+ Main.class, TestDiagnosticMessages::assertNoMessages, addDontWarn(MissingClass.class));
+ }
+
+ @Test
+ public void testIgnoreWarnings() throws Exception {
+ compileWithExpectedDiagnostics(
+ Main.class,
+ diagnostics -> inspectDiagnosticsWithIgnoreWarnings(diagnostics, referencedFrom),
+ addIgnoreWarnings());
+ }
+
+ static class Main {
+
+ public static void main(String[] args) {
+ MissingClass[] ignore = new MissingClass[0];
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/missingclasses/MissingClassReferencedFromNewInstanceTest.java b/src/test/java/com/android/tools/r8/missingclasses/MissingClassReferencedFromNewInstanceTest.java
index 8f43ab8..2af5045 100644
--- a/src/test/java/com/android/tools/r8/missingclasses/MissingClassReferencedFromNewInstanceTest.java
+++ b/src/test/java/com/android/tools/r8/missingclasses/MissingClassReferencedFromNewInstanceTest.java
@@ -4,51 +4,47 @@
package com.android.tools.r8.missingclasses;
-import static com.android.tools.r8.DiagnosticsMatcher.diagnosticType;
-import static org.junit.Assert.assertEquals;
-
+import com.android.tools.r8.CompilationFailedException;
import com.android.tools.r8.TestDiagnosticMessages;
import com.android.tools.r8.TestParameters;
-import com.android.tools.r8.references.TypeReference;
-import com.android.tools.r8.shaking.MissingClassesDiagnostic;
-import com.android.tools.r8.utils.codeinspector.AssertUtils;
+import com.android.tools.r8.references.MethodReference;
+import com.android.tools.r8.references.Reference;
+import com.android.tools.r8.utils.MethodReferenceUtils;
import org.junit.Test;
public class MissingClassReferencedFromNewInstanceTest extends MissingClassesTestBase {
- public MissingClassReferencedFromNewInstanceTest(
- DontWarnConfiguration dontWarnConfiguration, TestParameters parameters) {
- super(dontWarnConfiguration, parameters);
+ private static final MethodReference referencedFrom =
+ MethodReferenceUtils.mainMethod(Reference.classFromClass(Main.class));
+
+ public MissingClassReferencedFromNewInstanceTest(TestParameters parameters) {
+ super(parameters);
+ }
+
+ @Test(expected = CompilationFailedException.class)
+ public void testNoRules() throws Exception {
+ compileWithExpectedDiagnostics(
+ Main.class, diagnostics -> inspectDiagnosticsWithNoRules(diagnostics, referencedFrom));
}
@Test
- public void test() throws Exception {
- AssertUtils.assertFailsCompilationIf(
- // TODO(b/175542052): Should not fail compilation with -dontwarn Main.
- !getDontWarnConfiguration().isDontWarnMissingClass(),
- () ->
- compileWithExpectedDiagnostics(
- Main.class, MissingClass.class, this::inspectDiagnostics));
+ public void testDontWarnMainClass() throws Exception {
+ compileWithExpectedDiagnostics(
+ Main.class, TestDiagnosticMessages::assertNoMessages, addDontWarn(Main.class));
}
- private void inspectDiagnostics(TestDiagnosticMessages diagnostics) {
- // TODO(b/175542052): Should also not have any diagnostics with -dontwarn Main.
- if (getDontWarnConfiguration().isDontWarnMissingClass()) {
- diagnostics.assertNoMessages();
- return;
- }
+ @Test
+ public void testDontWarnMissingClass() throws Exception {
+ compileWithExpectedDiagnostics(
+ Main.class, TestDiagnosticMessages::assertNoMessages, addDontWarn(MissingClass.class));
+ }
- diagnostics
- .assertOnlyErrors()
- .assertErrorsCount(1)
- .assertAllErrorsMatch(diagnosticType(MissingClassesDiagnostic.class));
-
- MissingClassesDiagnostic diagnostic = (MissingClassesDiagnostic) diagnostics.getErrors().get(0);
- assertEquals(
- !getDontWarnConfiguration().isDontWarnMissingClass(),
- diagnostic.getMissingClasses().stream()
- .map(TypeReference::getTypeName)
- .anyMatch(MissingClass.class.getTypeName()::equals));
+ @Test
+ public void testIgnoreWarnings() throws Exception {
+ compileWithExpectedDiagnostics(
+ Main.class,
+ diagnostics -> inspectDiagnosticsWithIgnoreWarnings(diagnostics, referencedFrom),
+ addIgnoreWarnings());
}
static class Main {
@@ -57,6 +53,4 @@
new MissingClass();
}
}
-
- static class MissingClass {}
}
diff --git a/src/test/java/com/android/tools/r8/missingclasses/MissingClassReferencedFromOuterClassAttributeTest.java b/src/test/java/com/android/tools/r8/missingclasses/MissingClassReferencedFromOuterClassAttributeTest.java
new file mode 100644
index 0000000..d707f74
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/missingclasses/MissingClassReferencedFromOuterClassAttributeTest.java
@@ -0,0 +1,109 @@
+// 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.missingclasses;
+
+import static com.android.tools.r8.utils.codeinspector.AssertUtils.assertFailsCompilationIf;
+import static org.junit.Assert.assertFalse;
+
+import com.android.tools.r8.R8FullTestBuilder;
+import com.android.tools.r8.R8TestBuilder;
+import com.android.tools.r8.TestDiagnosticMessages;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.missingclasses.MissingClassReferencedFromOuterClassAttributeTest.MissingClass.Main;
+import com.android.tools.r8.references.ClassReference;
+import com.android.tools.r8.references.Reference;
+import com.android.tools.r8.utils.BooleanUtils;
+import java.util.List;
+import org.junit.Test;
+import org.junit.runners.Parameterized.Parameters;
+
+public class MissingClassReferencedFromOuterClassAttributeTest extends MissingClassesTestBase {
+
+ private static final ClassReference referencedFrom = Reference.classFromClass(Main.class);
+
+ @Parameters(name = "{1}, report: {0}")
+ public static List<Object[]> data() {
+ return buildParameters(
+ BooleanUtils.values(), getTestParameters().withAllRuntimesAndApiLevels().build());
+ }
+
+ private final boolean reportMissingClassesInInnerClassAttributes;
+
+ public MissingClassReferencedFromOuterClassAttributeTest(
+ boolean reportMissingClassesInInnerClassAttributes, TestParameters parameters) {
+ super(parameters);
+ this.reportMissingClassesInInnerClassAttributes = reportMissingClassesInInnerClassAttributes;
+ }
+
+ @Test
+ public void testNoRules() throws Exception {
+ assertFailsCompilationIf(
+ reportMissingClassesInInnerClassAttributes,
+ () ->
+ compileWithExpectedDiagnostics(
+ Main.class,
+ reportMissingClassesInInnerClassAttributes
+ ? diagnostics -> inspectDiagnosticsWithNoRules(diagnostics, referencedFrom)
+ : TestDiagnosticMessages::assertNoMessages,
+ this::configure));
+ }
+
+ @Test
+ public void testDontWarnMainClass() throws Exception {
+ compileWithExpectedDiagnostics(
+ Main.class,
+ TestDiagnosticMessages::assertNoMessages,
+ addDontWarn(Main.class).andThen(this::configure));
+ }
+
+ @Test
+ public void testDontWarnMissingClass() throws Exception {
+ compileWithExpectedDiagnostics(
+ Main.class,
+ TestDiagnosticMessages::assertNoMessages,
+ addDontWarn(MissingClass.class).andThen(this::configure));
+ }
+
+ @Test
+ public void testIgnoreWarnings() throws Exception {
+ compileWithExpectedDiagnostics(
+ Main.class,
+ reportMissingClassesInInnerClassAttributes
+ ? diagnostics -> inspectDiagnosticsWithIgnoreWarnings(diagnostics, referencedFrom)
+ : TestDiagnosticMessages::assertNoMessages,
+ addIgnoreWarnings(reportMissingClassesInInnerClassAttributes).andThen(this::configure));
+ }
+
+ void configure(R8FullTestBuilder builder) {
+ builder
+ .addKeepAttributeInnerClassesAndEnclosingMethod()
+ .addOptionsModification(
+ options -> {
+ // We do not report missing classes from inner class attributes by default.
+ assertFalse(options.reportMissingClassesInInnerClassAttributes);
+ options.reportMissingClassesInInnerClassAttributes =
+ reportMissingClassesInInnerClassAttributes;
+ })
+ .applyIf(
+ reportMissingClassesInInnerClassAttributes,
+ // We need to ignore the inner class attribute for the test class.
+ addDontWarn(MissingClassReferencedFromOuterClassAttributeTest.class),
+ // The -dontwarn Main and -dontwarn MissingClass tests will have unused -dontwarn rules.
+ R8TestBuilder::allowUnusedDontWarnPatterns);
+ }
+
+ @Override
+ ClassReference getMissingClassReference() {
+ return Reference.classFromClass(MissingClass.class);
+ }
+
+ static class MissingClass {
+
+ static class Main {
+
+ public static void main(String[] args) {}
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/missingclasses/MissingClassReferencedFromStaticGetToExistingFieldTest.java b/src/test/java/com/android/tools/r8/missingclasses/MissingClassReferencedFromStaticGetToExistingFieldTest.java
new file mode 100644
index 0000000..e76c4c9
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/missingclasses/MissingClassReferencedFromStaticGetToExistingFieldTest.java
@@ -0,0 +1,64 @@
+// 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.missingclasses;
+
+import com.android.tools.r8.CompilationFailedException;
+import com.android.tools.r8.TestDiagnosticMessages;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.references.FieldReference;
+import com.android.tools.r8.references.Reference;
+import org.junit.Test;
+
+/**
+ * If a field reference that refers to a missing class resolves to a definition, then the field
+ * definition is to be blamed.
+ */
+public class MissingClassReferencedFromStaticGetToExistingFieldTest extends MissingClassesTestBase {
+
+ private static final FieldReference referencedFrom =
+ Reference.field(
+ Reference.classFromClass(Main.class),
+ "FIELD",
+ Reference.classFromClass(MissingClass.class));
+
+ public MissingClassReferencedFromStaticGetToExistingFieldTest(TestParameters parameters) {
+ super(parameters);
+ }
+
+ @Test(expected = CompilationFailedException.class)
+ public void testNoRules() throws Exception {
+ compileWithExpectedDiagnostics(
+ Main.class, diagnostics -> inspectDiagnosticsWithNoRules(diagnostics, referencedFrom));
+ }
+
+ @Test
+ public void testDontWarnMainClass() throws Exception {
+ compileWithExpectedDiagnostics(
+ Main.class, TestDiagnosticMessages::assertNoMessages, addDontWarn(Main.class));
+ }
+
+ @Test
+ public void testDontWarnMissingClass() throws Exception {
+ compileWithExpectedDiagnostics(
+ Main.class, TestDiagnosticMessages::assertNoMessages, addDontWarn(MissingClass.class));
+ }
+
+ @Test
+ public void testIgnoreWarnings() throws Exception {
+ compileWithExpectedDiagnostics(
+ Main.class,
+ diagnostics -> inspectDiagnosticsWithIgnoreWarnings(diagnostics, referencedFrom),
+ addIgnoreWarnings());
+ }
+
+ static class Main {
+
+ public static MissingClass FIELD;
+
+ public static void main(String[] args) {
+ MissingClass ignore = FIELD;
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/missingclasses/MissingClassReferencedFromStaticGetToMissingFieldTest.java b/src/test/java/com/android/tools/r8/missingclasses/MissingClassReferencedFromStaticGetToMissingFieldTest.java
new file mode 100644
index 0000000..36f8a29
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/missingclasses/MissingClassReferencedFromStaticGetToMissingFieldTest.java
@@ -0,0 +1,60 @@
+// 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.missingclasses;
+
+import com.android.tools.r8.CompilationFailedException;
+import com.android.tools.r8.TestDiagnosticMessages;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.references.MethodReference;
+import com.android.tools.r8.references.Reference;
+import com.android.tools.r8.utils.MethodReferenceUtils;
+import org.junit.Test;
+
+/**
+ * If a field reference that refers to a missing class does not resolve, then the enclosing method
+ * is to be blamed.
+ */
+public class MissingClassReferencedFromStaticGetToMissingFieldTest extends MissingClassesTestBase {
+
+ private static final MethodReference referencedFrom =
+ MethodReferenceUtils.mainMethod(Reference.classFromClass(Main.class));
+
+ public MissingClassReferencedFromStaticGetToMissingFieldTest(TestParameters parameters) {
+ super(parameters);
+ }
+
+ @Test(expected = CompilationFailedException.class)
+ public void testNoRules() throws Exception {
+ compileWithExpectedDiagnostics(
+ Main.class, diagnostics -> inspectDiagnosticsWithNoRules(diagnostics, referencedFrom));
+ }
+
+ @Test
+ public void testDontWarnMainClass() throws Exception {
+ compileWithExpectedDiagnostics(
+ Main.class, TestDiagnosticMessages::assertNoMessages, addDontWarn(Main.class));
+ }
+
+ @Test
+ public void testDontWarnMissingClass() throws Exception {
+ compileWithExpectedDiagnostics(
+ Main.class, TestDiagnosticMessages::assertNoMessages, addDontWarn(MissingClass.class));
+ }
+
+ @Test
+ public void testIgnoreWarnings() throws Exception {
+ compileWithExpectedDiagnostics(
+ Main.class,
+ diagnostics -> inspectDiagnosticsWithIgnoreWarnings(diagnostics, referencedFrom),
+ addIgnoreWarnings());
+ }
+
+ static class Main {
+
+ public static void main(String[] args) {
+ int ignore = MissingClass.FIELD;
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/missingclasses/MissingClassReferencedFromStaticPutToExistingFieldTest.java b/src/test/java/com/android/tools/r8/missingclasses/MissingClassReferencedFromStaticPutToExistingFieldTest.java
new file mode 100644
index 0000000..542c928
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/missingclasses/MissingClassReferencedFromStaticPutToExistingFieldTest.java
@@ -0,0 +1,64 @@
+// 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.missingclasses;
+
+import com.android.tools.r8.CompilationFailedException;
+import com.android.tools.r8.TestDiagnosticMessages;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.references.FieldReference;
+import com.android.tools.r8.references.Reference;
+import org.junit.Test;
+
+/**
+ * If a field reference that refers to a missing class resolves to a definition, then the field
+ * definition is to be blamed.
+ */
+public class MissingClassReferencedFromStaticPutToExistingFieldTest extends MissingClassesTestBase {
+
+ private static final FieldReference referencedFrom =
+ Reference.field(
+ Reference.classFromClass(Main.class),
+ "FIELD",
+ Reference.classFromClass(MissingClass.class));
+
+ public MissingClassReferencedFromStaticPutToExistingFieldTest(TestParameters parameters) {
+ super(parameters);
+ }
+
+ @Test(expected = CompilationFailedException.class)
+ public void testNoRules() throws Exception {
+ compileWithExpectedDiagnostics(
+ Main.class, diagnostics -> inspectDiagnosticsWithNoRules(diagnostics, referencedFrom));
+ }
+
+ @Test
+ public void testDontWarnMainClass() throws Exception {
+ compileWithExpectedDiagnostics(
+ Main.class, TestDiagnosticMessages::assertNoMessages, addDontWarn(Main.class));
+ }
+
+ @Test
+ public void testDontWarnMissingClass() throws Exception {
+ compileWithExpectedDiagnostics(
+ Main.class, TestDiagnosticMessages::assertNoMessages, addDontWarn(MissingClass.class));
+ }
+
+ @Test
+ public void testIgnoreWarnings() throws Exception {
+ compileWithExpectedDiagnostics(
+ Main.class,
+ diagnostics -> inspectDiagnosticsWithIgnoreWarnings(diagnostics, referencedFrom),
+ addIgnoreWarnings());
+ }
+
+ static class Main {
+
+ public static MissingClass FIELD;
+
+ public static void main(String[] args) {
+ FIELD = null;
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/missingclasses/MissingClassReferencedFromStaticPutToMissingFieldTest.java b/src/test/java/com/android/tools/r8/missingclasses/MissingClassReferencedFromStaticPutToMissingFieldTest.java
new file mode 100644
index 0000000..542d509
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/missingclasses/MissingClassReferencedFromStaticPutToMissingFieldTest.java
@@ -0,0 +1,60 @@
+// 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.missingclasses;
+
+import com.android.tools.r8.CompilationFailedException;
+import com.android.tools.r8.TestDiagnosticMessages;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.references.MethodReference;
+import com.android.tools.r8.references.Reference;
+import com.android.tools.r8.utils.MethodReferenceUtils;
+import org.junit.Test;
+
+/**
+ * If a field reference that refers to a missing class does not resolve, then the enclosing method
+ * is to be blamed.
+ */
+public class MissingClassReferencedFromStaticPutToMissingFieldTest extends MissingClassesTestBase {
+
+ private static final MethodReference referencedFrom =
+ MethodReferenceUtils.mainMethod(Reference.classFromClass(Main.class));
+
+ public MissingClassReferencedFromStaticPutToMissingFieldTest(TestParameters parameters) {
+ super(parameters);
+ }
+
+ @Test(expected = CompilationFailedException.class)
+ public void testNoRules() throws Exception {
+ compileWithExpectedDiagnostics(
+ Main.class, diagnostics -> inspectDiagnosticsWithNoRules(diagnostics, referencedFrom));
+ }
+
+ @Test
+ public void testDontWarnMainClass() throws Exception {
+ compileWithExpectedDiagnostics(
+ Main.class, TestDiagnosticMessages::assertNoMessages, addDontWarn(Main.class));
+ }
+
+ @Test
+ public void testDontWarnMissingClass() throws Exception {
+ compileWithExpectedDiagnostics(
+ Main.class, TestDiagnosticMessages::assertNoMessages, addDontWarn(MissingClass.class));
+ }
+
+ @Test
+ public void testIgnoreWarnings() throws Exception {
+ compileWithExpectedDiagnostics(
+ Main.class,
+ diagnostics -> inspectDiagnosticsWithIgnoreWarnings(diagnostics, referencedFrom),
+ addIgnoreWarnings());
+ }
+
+ static class Main {
+
+ public static void main(String[] args) {
+ MissingClass.FIELD = 42;
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/missingclasses/MissingClassReferencedFromSuperClassTest.java b/src/test/java/com/android/tools/r8/missingclasses/MissingClassReferencedFromSuperClassTest.java
new file mode 100644
index 0000000..7aeb068
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/missingclasses/MissingClassReferencedFromSuperClassTest.java
@@ -0,0 +1,62 @@
+// 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.missingclasses;
+
+import com.android.tools.r8.CompilationFailedException;
+import com.android.tools.r8.TestCompilerBuilder;
+import com.android.tools.r8.TestDiagnosticMessages;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.references.ClassReference;
+import com.android.tools.r8.references.Reference;
+import org.junit.Test;
+
+public class MissingClassReferencedFromSuperClassTest extends MissingClassesTestBase {
+
+ private static final ClassReference referencedFrom = Reference.classFromClass(Main.class);
+
+ public MissingClassReferencedFromSuperClassTest(TestParameters parameters) {
+ super(parameters);
+ }
+
+ // The tests explicitly disable desugaring to prevent desugaring warnings.
+ // TODO(b/179341237): Consider if desugaring warnings should be D8 only, since in a way they are
+ // all duplicates of the MissingClassesDiagnostic.
+ @Test(expected = CompilationFailedException.class)
+ public void testNoRules() throws Exception {
+ compileWithExpectedDiagnostics(
+ Main.class,
+ diagnostics -> inspectDiagnosticsWithNoRules(diagnostics, referencedFrom),
+ TestCompilerBuilder::disableDesugaring);
+ }
+
+ @Test
+ public void testDontWarnMainClass() throws Exception {
+ compileWithExpectedDiagnostics(
+ Main.class,
+ TestDiagnosticMessages::assertNoMessages,
+ addDontWarn(Main.class).andThen(TestCompilerBuilder::disableDesugaring));
+ }
+
+ @Test
+ public void testDontWarnMissingClass() throws Exception {
+ compileWithExpectedDiagnostics(
+ Main.class,
+ TestDiagnosticMessages::assertNoMessages,
+ addDontWarn(MissingClass.class).andThen(TestCompilerBuilder::disableDesugaring));
+ }
+
+ @Test
+ public void testIgnoreWarnings() throws Exception {
+ compileWithExpectedDiagnostics(
+ Main.class,
+ diagnostics -> inspectDiagnosticsWithIgnoreWarnings(diagnostics, referencedFrom),
+ addIgnoreWarnings().andThen(TestCompilerBuilder::disableDesugaring));
+ }
+
+ static class Main extends MissingClass {
+
+ public static void main(String[] args) {}
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/missingclasses/MissingClassReferencedFromThrowsClauseTest.java b/src/test/java/com/android/tools/r8/missingclasses/MissingClassReferencedFromThrowsClauseTest.java
new file mode 100644
index 0000000..1708330
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/missingclasses/MissingClassReferencedFromThrowsClauseTest.java
@@ -0,0 +1,61 @@
+// 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.missingclasses;
+
+import com.android.tools.r8.CompilationFailedException;
+import com.android.tools.r8.TestDiagnosticMessages;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestShrinkerBuilder;
+import com.android.tools.r8.references.MethodReference;
+import com.android.tools.r8.references.Reference;
+import com.android.tools.r8.utils.MethodReferenceUtils;
+import org.junit.Test;
+
+public class MissingClassReferencedFromThrowsClauseTest extends MissingClassesTestBase {
+
+ private static final MethodReference referencedFrom =
+ MethodReferenceUtils.mainMethod(Reference.classFromClass(Main.class));
+
+ public MissingClassReferencedFromThrowsClauseTest(TestParameters parameters) {
+ super(parameters);
+ }
+
+ @Test(expected = CompilationFailedException.class)
+ public void testNoRules() throws Exception {
+ compileWithExpectedDiagnostics(
+ Main.class,
+ diagnostics -> inspectDiagnosticsWithNoRules(diagnostics, referencedFrom),
+ TestShrinkerBuilder::addKeepAttributeExceptions);
+ }
+
+ @Test
+ public void testDontWarnMainClass() throws Exception {
+ compileWithExpectedDiagnostics(
+ Main.class,
+ TestDiagnosticMessages::assertNoMessages,
+ addDontWarn(Main.class).andThen(TestShrinkerBuilder::addKeepAttributeExceptions));
+ }
+
+ @Test
+ public void testDontWarnMissingClass() throws Exception {
+ compileWithExpectedDiagnostics(
+ Main.class,
+ TestDiagnosticMessages::assertNoMessages,
+ addDontWarn(MissingClass.class).andThen(TestShrinkerBuilder::addKeepAttributeExceptions));
+ }
+
+ @Test
+ public void testIgnoreWarnings() throws Exception {
+ compileWithExpectedDiagnostics(
+ Main.class,
+ diagnostics -> inspectDiagnosticsWithIgnoreWarnings(diagnostics, referencedFrom),
+ addIgnoreWarnings().andThen(TestShrinkerBuilder::addKeepAttributeExceptions));
+ }
+
+ static class Main {
+
+ public static void main(String[] args) throws MissingClass {}
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/missingclasses/MissingClassesTestBase.java b/src/test/java/com/android/tools/r8/missingclasses/MissingClassesTestBase.java
index 7c72273..dafb7b4 100644
--- a/src/test/java/com/android/tools/r8/missingclasses/MissingClassesTestBase.java
+++ b/src/test/java/com/android/tools/r8/missingclasses/MissingClassesTestBase.java
@@ -4,13 +4,27 @@
package com.android.tools.r8.missingclasses;
+import static com.android.tools.r8.DiagnosticsMatcher.diagnosticType;
+import static org.junit.Assert.assertEquals;
+
import com.android.tools.r8.CompilationFailedException;
-import com.android.tools.r8.R8TestCompileResult;
+import com.android.tools.r8.R8FullTestBuilder;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestCompilerBuilder.DiagnosticsConsumer;
+import com.android.tools.r8.TestDiagnosticMessages;
import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.ThrowableConsumer;
+import com.android.tools.r8.references.ClassReference;
+import com.android.tools.r8.references.FieldReference;
+import com.android.tools.r8.references.MethodReference;
+import com.android.tools.r8.references.Reference;
+import com.android.tools.r8.shaking.MissingClassesDiagnostic;
+import com.android.tools.r8.utils.FieldReferenceUtils;
import com.android.tools.r8.utils.InternalOptions.TestingOptions;
+import com.android.tools.r8.utils.MethodReferenceUtils;
+import com.google.common.collect.ImmutableSet;
import java.util.List;
+import java.util.function.Function;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import org.junit.runners.Parameterized.Parameters;
@@ -18,53 +32,164 @@
@RunWith(Parameterized.class)
public abstract class MissingClassesTestBase extends TestBase {
- enum DontWarnConfiguration {
- DONT_WARN_MAIN_CLASS,
- DONT_WARN_MISSING_CLASS,
- NONE;
+ static class MissingClass extends RuntimeException {
- public boolean isDontWarnMainClass() {
- return this == DONT_WARN_MAIN_CLASS;
- }
+ static int FIELD;
- public boolean isDontWarnMissingClass() {
- return this == DONT_WARN_MISSING_CLASS;
- }
+ int field;
}
- private final DontWarnConfiguration dontWarnConfiguration;
+ interface MissingInterface {}
+
private final TestParameters parameters;
- @Parameters(name = "{1}, {0}")
+ @Parameters(name = "{0}")
public static List<Object[]> data() {
- return buildParameters(
- DontWarnConfiguration.values(), getTestParameters().withAllRuntimesAndApiLevels().build());
+ return buildParameters(getTestParameters().withAllRuntimesAndApiLevels().build());
}
- public MissingClassesTestBase(
- DontWarnConfiguration dontWarnConfiguration, TestParameters parameters) {
- this.dontWarnConfiguration = dontWarnConfiguration;
+ public MissingClassesTestBase(TestParameters parameters) {
this.parameters = parameters;
}
- public R8TestCompileResult compileWithExpectedDiagnostics(
- Class<?> mainClass, Class<?> missingClass, DiagnosticsConsumer diagnosticsConsumer)
+ public ThrowableConsumer<R8FullTestBuilder> addDontWarn(Class<?> clazz) {
+ return builder -> builder.addDontWarn(clazz);
+ }
+
+ public ThrowableConsumer<R8FullTestBuilder> addIgnoreWarnings() {
+ return addIgnoreWarnings(true);
+ }
+
+ public ThrowableConsumer<R8FullTestBuilder> addIgnoreWarnings(
+ boolean allowDiagnosticWarningMessages) {
+ return builder ->
+ builder.addIgnoreWarnings().allowDiagnosticWarningMessages(allowDiagnosticWarningMessages);
+ }
+
+ public void compileWithExpectedDiagnostics(
+ Class<?> mainClass, DiagnosticsConsumer diagnosticsConsumer)
throws CompilationFailedException {
- return testForR8(parameters.getBackend())
- .addProgramClasses(mainClass)
- .addKeepMainRule(mainClass)
- .applyIf(
- dontWarnConfiguration.isDontWarnMainClass(),
- testBuilder -> testBuilder.addDontWarn(mainClass))
- .applyIf(
- dontWarnConfiguration.isDontWarnMissingClass(),
- testBuilder -> testBuilder.addDontWarn(missingClass))
+ compileWithExpectedDiagnostics(mainClass, diagnosticsConsumer, null);
+ }
+
+ public void compileWithExpectedDiagnostics(
+ Class<?> mainClass,
+ DiagnosticsConsumer diagnosticsConsumer,
+ ThrowableConsumer<R8FullTestBuilder> configuration)
+ throws CompilationFailedException {
+ internalCompileWithExpectedDiagnostics(
+ diagnosticsConsumer,
+ builder ->
+ builder.addProgramClasses(mainClass).addKeepMainRule(mainClass).apply(configuration));
+ }
+
+ public void compileWithExpectedDiagnostics(
+ ThrowableConsumer<R8FullTestBuilder> configuration, DiagnosticsConsumer diagnosticsConsumer)
+ throws CompilationFailedException {
+ internalCompileWithExpectedDiagnostics(diagnosticsConsumer, configuration);
+ }
+
+ private void internalCompileWithExpectedDiagnostics(
+ DiagnosticsConsumer diagnosticsConsumer, ThrowableConsumer<R8FullTestBuilder> configuration)
+ throws CompilationFailedException {
+ testForR8(parameters.getBackend())
+ .apply(configuration)
.addOptionsModification(TestingOptions::enableExperimentalMissingClassesReporting)
.setMinApi(parameters.getApiLevel())
.compileWithExpectedDiagnostics(diagnosticsConsumer);
}
- public DontWarnConfiguration getDontWarnConfiguration() {
- return dontWarnConfiguration;
+ ClassReference getMissingClassReference() {
+ return Reference.classFromClass(MissingClass.class);
+ }
+
+ void inspectDiagnosticsWithIgnoreWarnings(
+ TestDiagnosticMessages diagnostics, ClassReference referencedFrom) {
+ inspectDiagnosticsWithIgnoreWarnings(
+ diagnostics,
+ getExpectedDiagnosticMessageWithIgnoreWarnings(
+ referencedFrom, ClassReference::getTypeName));
+ }
+
+ void inspectDiagnosticsWithIgnoreWarnings(
+ TestDiagnosticMessages diagnostics, FieldReference referencedFrom) {
+ inspectDiagnosticsWithIgnoreWarnings(
+ diagnostics,
+ getExpectedDiagnosticMessageWithIgnoreWarnings(
+ referencedFrom, FieldReferenceUtils::toSourceString));
+ }
+
+ void inspectDiagnosticsWithIgnoreWarnings(
+ TestDiagnosticMessages diagnostics, MethodReference referencedFrom) {
+ inspectDiagnosticsWithIgnoreWarnings(
+ diagnostics,
+ getExpectedDiagnosticMessageWithIgnoreWarnings(
+ referencedFrom, MethodReferenceUtils::toSourceString));
+ }
+
+ void inspectDiagnosticsWithIgnoreWarnings(
+ TestDiagnosticMessages diagnostics, String expectedDiagnosticMessage) {
+ MissingClassesDiagnostic diagnostic =
+ diagnostics
+ .assertOnlyWarnings()
+ .assertWarningsCount(1)
+ .assertAllWarningsMatch(diagnosticType(MissingClassesDiagnostic.class))
+ .getWarning(0);
+ assertEquals(ImmutableSet.of(getMissingClassReference()), diagnostic.getMissingClasses());
+ assertEquals(expectedDiagnosticMessage, diagnostic.getDiagnosticMessage());
+ }
+
+ private <T> String getExpectedDiagnosticMessageWithIgnoreWarnings(
+ T referencedFrom, Function<T, String> toSourceStringFunction) {
+ return "Missing class "
+ + getMissingClassReference().getTypeName()
+ + " (referenced from: "
+ + toSourceStringFunction.apply(referencedFrom)
+ + ")";
+ }
+
+ void inspectDiagnosticsWithNoRules(
+ TestDiagnosticMessages diagnostics, ClassReference referencedFrom) {
+ inspectDiagnosticsWithNoRules(
+ diagnostics,
+ getExpectedDiagnosticMessageWithNoRules(referencedFrom, ClassReference::getTypeName));
+ }
+
+ void inspectDiagnosticsWithNoRules(
+ TestDiagnosticMessages diagnostics, FieldReference referencedFrom) {
+ inspectDiagnosticsWithNoRules(
+ diagnostics,
+ getExpectedDiagnosticMessageWithNoRules(
+ referencedFrom, FieldReferenceUtils::toSourceString));
+ }
+
+ void inspectDiagnosticsWithNoRules(
+ TestDiagnosticMessages diagnostics, MethodReference referencedFrom) {
+ inspectDiagnosticsWithNoRules(
+ diagnostics,
+ getExpectedDiagnosticMessageWithNoRules(
+ referencedFrom, MethodReferenceUtils::toSourceString));
+ }
+
+ void inspectDiagnosticsWithNoRules(
+ TestDiagnosticMessages diagnostics, String expectedDiagnosticMessage) {
+ MissingClassesDiagnostic diagnostic =
+ diagnostics
+ .assertOnlyErrors()
+ .assertErrorsCount(1)
+ .assertAllErrorsMatch(diagnosticType(MissingClassesDiagnostic.class))
+ .getError(0);
+ assertEquals(ImmutableSet.of(getMissingClassReference()), diagnostic.getMissingClasses());
+ assertEquals(expectedDiagnosticMessage, diagnostic.getDiagnosticMessage());
+ }
+
+ private <T> String getExpectedDiagnosticMessageWithNoRules(
+ T referencedFrom, Function<T, String> toSourceStringFunction) {
+ return "Compilation can't be completed because the following class is missing: "
+ + getMissingClassReference().getTypeName()
+ + " (referenced from: "
+ + toSourceStringFunction.apply(referencedFrom)
+ + ")"
+ + ".";
}
}
diff --git a/src/test/java/com/android/tools/r8/naming/AbstractR8KotlinNamingTestBase.java b/src/test/java/com/android/tools/r8/naming/AbstractR8KotlinNamingTestBase.java
index ddc2da6..2c3f2bb 100644
--- a/src/test/java/com/android/tools/r8/naming/AbstractR8KotlinNamingTestBase.java
+++ b/src/test/java/com/android/tools/r8/naming/AbstractR8KotlinNamingTestBase.java
@@ -7,10 +7,8 @@
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresentAndRenamed;
import static org.hamcrest.MatcherAssert.assertThat;
-import com.android.tools.r8.KotlinCompilerTool.KotlinCompiler;
-import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
+import com.android.tools.r8.KotlinTestParameters;
import com.android.tools.r8.kotlin.AbstractR8KotlinTestBase;
-import com.android.tools.r8.naming.MemberNaming.MethodSignature;
import com.android.tools.r8.utils.codeinspector.ClassSubject;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
import com.android.tools.r8.utils.codeinspector.FieldSubject;
@@ -21,11 +19,10 @@
protected final boolean minification;
AbstractR8KotlinNamingTestBase(
- KotlinTargetVersion kotlinTargetVersion,
- KotlinCompiler kotlinc,
+ KotlinTestParameters kotlinParameters,
boolean allowAccessModification,
boolean minification) {
- super(kotlinTargetVersion, kotlinc, allowAccessModification);
+ super(kotlinParameters, allowAccessModification);
this.minification = minification;
}
@@ -41,56 +38,27 @@
return classSubject;
}
- protected FieldSubject checkFieldIsRenamed(
- ClassSubject classSubject, String fieldType, String fieldName) {
- FieldSubject fieldSubject = checkFieldIsKept(classSubject, fieldType, fieldName);
- assertThat(fieldSubject, isPresentAndRenamed());
- return fieldSubject;
- }
-
protected FieldSubject checkFieldIsRenamed(ClassSubject classSubject, String fieldName) {
FieldSubject fieldSubject = checkFieldIsKept(classSubject, fieldName);
assertThat(fieldSubject, isPresentAndRenamed());
return fieldSubject;
}
- protected FieldSubject checkFieldIsNotRenamed(
- ClassSubject classSubject, String fieldType, String fieldName) {
- FieldSubject fieldSubject = checkFieldIsKept(classSubject, fieldType, fieldName);
- assertThat(fieldSubject, isPresentAndNotRenamed());
- return fieldSubject;
- }
-
protected FieldSubject checkFieldIsNotRenamed(ClassSubject classSubject, String fieldName) {
FieldSubject fieldSubject = checkFieldIsKept(classSubject, fieldName);
assertThat(fieldSubject, isPresentAndNotRenamed());
return fieldSubject;
}
- protected MethodSubject checkMethodIsRenamed(
- ClassSubject classSubject, MethodSignature methodSignature) {
- MethodSubject methodSubject = checkMethodIsKept(classSubject, methodSignature);
- assertThat(methodSubject, isPresentAndRenamed());
- return methodSubject;
- }
-
protected MethodSubject checkMethodIsRenamed(ClassSubject classSubject, String methodName) {
MethodSubject methodSubject = checkMethodIsKept(classSubject, methodName);
assertThat(methodSubject, isPresentAndRenamed());
return methodSubject;
}
- protected MethodSubject checkMethodIsNotRenamed(
- ClassSubject classSubject, MethodSignature methodSignature) {
- MethodSubject methodSubject = checkMethodIsKept(classSubject, methodSignature);
- assertThat(methodSubject, isPresentAndNotRenamed());
- return methodSubject;
- }
-
protected MethodSubject checkMethodIsNotRenamed(ClassSubject classSubject, String methodName) {
MethodSubject methodSubject = checkMethodIsKept(classSubject, methodName);
assertThat(methodSubject, isPresentAndNotRenamed());
return methodSubject;
}
-
}
diff --git a/src/test/java/com/android/tools/r8/naming/EnumMinificationKotlinTest.java b/src/test/java/com/android/tools/r8/naming/EnumMinificationKotlinTest.java
index b96ff51..f911ce6 100644
--- a/src/test/java/com/android/tools/r8/naming/EnumMinificationKotlinTest.java
+++ b/src/test/java/com/android/tools/r8/naming/EnumMinificationKotlinTest.java
@@ -3,17 +3,15 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.naming;
-import static com.android.tools.r8.ToolHelper.getKotlinCompilers;
import static com.android.tools.r8.utils.codeinspector.Matchers.isAbsent;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
import static org.hamcrest.CoreMatchers.equalTo;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.Assert.assertEquals;
-import com.android.tools.r8.KotlinCompilerTool.KotlinCompiler;
import com.android.tools.r8.KotlinTestBase;
+import com.android.tools.r8.KotlinTestParameters;
import com.android.tools.r8.TestParameters;
-import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
import com.android.tools.r8.utils.BooleanUtils;
import com.android.tools.r8.utils.codeinspector.ClassSubject;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
@@ -31,21 +29,17 @@
private final TestParameters parameters;
private final boolean minify;
- @Parameterized.Parameters(name = "{0}, target: {1}, kotlinc: {2}, minify: {3}")
+ @Parameterized.Parameters(name = "{0}, {1}, minify: {2}")
public static Collection<Object[]> data() {
return buildParameters(
getTestParameters().withAllRuntimesAndApiLevels().build(),
- KotlinTargetVersion.values(),
- getKotlinCompilers(),
+ getKotlinTestParameters().withAllCompilersAndTargetVersions().build(),
BooleanUtils.values());
}
public EnumMinificationKotlinTest(
- TestParameters parameters,
- KotlinTargetVersion targetVersion,
- KotlinCompiler kotlinc,
- boolean minify) {
- super(targetVersion, kotlinc);
+ TestParameters parameters, KotlinTestParameters kotlinParameters, boolean minify) {
+ super(kotlinParameters);
this.parameters = parameters;
this.minify = minify;
}
diff --git a/src/test/java/com/android/tools/r8/naming/KotlinIntrinsicsIdentifierTest.java b/src/test/java/com/android/tools/r8/naming/KotlinIntrinsicsIdentifierTest.java
index 7a1b6e4..5d9e78b 100644
--- a/src/test/java/com/android/tools/r8/naming/KotlinIntrinsicsIdentifierTest.java
+++ b/src/test/java/com/android/tools/r8/naming/KotlinIntrinsicsIdentifierTest.java
@@ -3,17 +3,15 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.naming;
-import static com.android.tools.r8.ToolHelper.getKotlinCompilers;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
import static org.hamcrest.CoreMatchers.equalTo;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
-import com.android.tools.r8.KotlinCompilerTool.KotlinCompiler;
+import com.android.tools.r8.KotlinTestParameters;
import com.android.tools.r8.SingleTestRunResult;
import com.android.tools.r8.TestCompileResult;
-import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.kotlin.TestKotlinClass;
@@ -39,21 +37,19 @@
public class KotlinIntrinsicsIdentifierTest extends AbstractR8KotlinNamingTestBase {
private static final String FOLDER = "intrinsics_identifiers";
- @Parameters(name = "target: {0}, kotlinc: {1}, allowAccessModification: {2}, minification: {3}")
+ @Parameters(name = "{0}, allowAccessModification: {1}, minification: {2}")
public static Collection<Object[]> data() {
return buildParameters(
- KotlinTargetVersion.values(),
- getKotlinCompilers(),
+ getKotlinTestParameters().withAllCompilersAndTargetVersions().build(),
BooleanUtils.values(),
BooleanUtils.values());
}
public KotlinIntrinsicsIdentifierTest(
- KotlinTargetVersion targetVersion,
- KotlinCompiler kotlinc,
+ KotlinTestParameters kotlinParameters,
boolean allowAccessModification,
boolean minification) {
- super(targetVersion, kotlinc, allowAccessModification, minification);
+ super(kotlinParameters, allowAccessModification, minification);
}
private static final KotlinCompileMemoizer compiledJars =
diff --git a/src/test/java/com/android/tools/r8/naming/b139991218/TestRunner.java b/src/test/java/com/android/tools/r8/naming/b139991218/TestRunner.java
index 69f0148..665e410 100644
--- a/src/test/java/com/android/tools/r8/naming/b139991218/TestRunner.java
+++ b/src/test/java/com/android/tools/r8/naming/b139991218/TestRunner.java
@@ -4,8 +4,8 @@
package com.android.tools.r8.naming.b139991218;
-import static junit.framework.TestCase.assertTrue;
-import static org.junit.Assert.assertFalse;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.hamcrest.MatcherAssert.assertThat;
import com.android.tools.r8.CompilationFailedException;
import com.android.tools.r8.TestBase;
@@ -14,8 +14,6 @@
import com.android.tools.r8.ToolHelper;
import com.android.tools.r8.ir.optimize.Inliner.Reason;
import com.android.tools.r8.utils.FileUtils;
-import com.android.tools.r8.utils.StringUtils;
-import com.android.tools.r8.utils.codeinspector.FoundClassSubject;
import com.google.common.collect.ImmutableSet;
import java.io.IOException;
import java.nio.file.Paths;
@@ -63,21 +61,25 @@
options -> {
options.testing.validInliningReasons = ImmutableSet.of(Reason.FORCE);
options.enableClassInlining = false;
+
+ // TODO(b/179019716): Add support for merging in presence of annotations.
+ options.horizontalClassMergerOptions()
+ .skipNoClassesOrMembersWithAnnotationsPolicyForTesting =
+ true;
})
.addDontWarnJetBrainsAnnotations()
+ .addHorizontallyMergedClassesInspector(
+ inspector ->
+ inspector.assertIsCompleteMergeGroup(
+ "com.android.tools.r8.naming.b139991218.Lambda1",
+ "com.android.tools.r8.naming.b139991218.Lambda2"))
.setMinApi(parameters.getApiLevel())
.run(parameters.getRuntime(), Main.class)
- .assertSuccessWithOutput(StringUtils.lines("11", "12"))
+ .assertSuccessWithOutputLines("11", "12")
.inspect(
- inspector -> {
- // Ensure that we have created a lambda group and that the lambda classes are now
- // gone.
- boolean foundLambdaGroup = false;
- for (FoundClassSubject allClass : inspector.allClasses()) {
- foundLambdaGroup |= allClass.getOriginalName().contains("LambdaGroup");
- assertFalse(allClass.getOriginalName().contains("b139991218.Lambda"));
- }
- assertTrue(foundLambdaGroup);
- });
+ inspector ->
+ assertThat(
+ inspector.clazz("com.android.tools.r8.naming.b139991218.Lambda1"),
+ isPresent()));
}
}
diff --git a/src/test/java/com/android/tools/r8/regress/b113347830/B113347830.java b/src/test/java/com/android/tools/r8/regress/b113347830/B113347830.java
index 397f703..942ae66 100644
--- a/src/test/java/com/android/tools/r8/regress/b113347830/B113347830.java
+++ b/src/test/java/com/android/tools/r8/regress/b113347830/B113347830.java
@@ -48,7 +48,7 @@
testForD8(Backend.DEX)
.addProgramClassFileData(bytes)
- .setDisableDesugaring(true)
+ .disableDesugaring()
.setProgramConsumer(DexIndexedConsumer.emptyConsumer())
.addOptionsModification(options -> options.testing.disableStackMapVerification = true)
.compile();
diff --git a/src/test/java/com/android/tools/r8/regress/b149890887/MissingLibraryTargetTest.java b/src/test/java/com/android/tools/r8/regress/b149890887/MissingLibraryTargetTest.java
index 927decb..d01d917 100644
--- a/src/test/java/com/android/tools/r8/regress/b149890887/MissingLibraryTargetTest.java
+++ b/src/test/java/com/android/tools/r8/regress/b149890887/MissingLibraryTargetTest.java
@@ -46,10 +46,6 @@
.build()));
}
- private boolean isDesugaring() {
- return parameters.isDexRuntime() && parameters.getApiLevel().isLessThan(AndroidApiLevel.N);
- }
-
@Test
public void testReference() throws Exception {
testForRuntime(parameters)
diff --git a/src/test/java/com/android/tools/r8/regress/b77842465/Regress77842465.java b/src/test/java/com/android/tools/r8/regress/b77842465/Regress77842465.java
index 196ca52..cd3743f 100644
--- a/src/test/java/com/android/tools/r8/regress/b77842465/Regress77842465.java
+++ b/src/test/java/com/android/tools/r8/regress/b77842465/Regress77842465.java
@@ -17,7 +17,7 @@
public void test() throws CompilationFailedException, IOException {
testForD8()
.addProgramClassFileData(Regress77842465Dump.dump())
- .noDesugaring()
+ .disableDesugaring()
.setMinApi(AndroidApiLevel.M)
.compile()
.runDex2Oat(new DexRuntime(ToolHelper.getDexVm()))
diff --git a/src/test/java/com/android/tools/r8/retrace/KotlinInlineFunctionInSameFileRetraceTests.java b/src/test/java/com/android/tools/r8/retrace/KotlinInlineFunctionInSameFileRetraceTests.java
index 94e1e1a..2479dce 100644
--- a/src/test/java/com/android/tools/r8/retrace/KotlinInlineFunctionInSameFileRetraceTests.java
+++ b/src/test/java/com/android/tools/r8/retrace/KotlinInlineFunctionInSameFileRetraceTests.java
@@ -5,7 +5,6 @@
import static com.android.tools.r8.Collectors.toSingle;
import static com.android.tools.r8.ToolHelper.getFilesInTestFolderRelativeToClass;
-import static com.android.tools.r8.ToolHelper.getKotlinCompilers;
import static com.android.tools.r8.utils.codeinspector.Matchers.containsLinePositions;
import static com.android.tools.r8.utils.codeinspector.Matchers.isInlineFrame;
import static com.android.tools.r8.utils.codeinspector.Matchers.isInlineStack;
@@ -16,11 +15,10 @@
import com.android.tools.r8.CompilationFailedException;
import com.android.tools.r8.CompilationMode;
-import com.android.tools.r8.KotlinCompilerTool.KotlinCompiler;
import com.android.tools.r8.KotlinTestBase;
+import com.android.tools.r8.KotlinTestParameters;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.ToolHelper;
-import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
import com.android.tools.r8.naming.retrace.StackTrace;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
import com.android.tools.r8.utils.codeinspector.InstructionSubject;
@@ -44,18 +42,17 @@
private final TestParameters parameters;
- @Parameters(name = "{0}")
+ @Parameters(name = "{0}, {1}")
public static List<Object[]> data() {
// TODO(b/141817471): Extend with compilation modes.
return buildParameters(
getTestParameters().withAllRuntimesAndApiLevels().build(),
- KotlinTargetVersion.values(),
- getKotlinCompilers());
+ getKotlinTestParameters().withAllCompilersAndTargetVersions().build());
}
public KotlinInlineFunctionInSameFileRetraceTests(
- TestParameters parameters, KotlinTargetVersion targetVersion, KotlinCompiler kotlinc) {
- super(targetVersion, kotlinc);
+ TestParameters parameters, KotlinTestParameters kotlinParameters) {
+ super(kotlinParameters);
this.parameters = parameters;
}
diff --git a/src/test/java/com/android/tools/r8/retrace/KotlinInlineFunctionRetraceTest.java b/src/test/java/com/android/tools/r8/retrace/KotlinInlineFunctionRetraceTest.java
index 3827c26..945b732 100644
--- a/src/test/java/com/android/tools/r8/retrace/KotlinInlineFunctionRetraceTest.java
+++ b/src/test/java/com/android/tools/r8/retrace/KotlinInlineFunctionRetraceTest.java
@@ -6,7 +6,6 @@
import static com.android.tools.r8.Collectors.toSingle;
import static com.android.tools.r8.ToolHelper.getFilesInTestFolderRelativeToClass;
-import static com.android.tools.r8.ToolHelper.getKotlinCompilers;
import static com.android.tools.r8.utils.codeinspector.Matchers.containsLinePositions;
import static com.android.tools.r8.utils.codeinspector.Matchers.isInlineFrame;
import static com.android.tools.r8.utils.codeinspector.Matchers.isInlineStack;
@@ -17,11 +16,10 @@
import com.android.tools.r8.CompilationFailedException;
import com.android.tools.r8.CompilationMode;
-import com.android.tools.r8.KotlinCompilerTool.KotlinCompiler;
import com.android.tools.r8.KotlinTestBase;
+import com.android.tools.r8.KotlinTestParameters;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.ToolHelper;
-import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
import com.android.tools.r8.naming.retrace.StackTrace;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
import com.android.tools.r8.utils.codeinspector.FoundMethodSubject;
@@ -45,18 +43,17 @@
private static final String FILENAME_INLINE_STATIC = "InlineFunction.kt";
private static final String FILENAME_INLINE_INSTANCE = "InlineFunction.kt";
- @Parameters(name = "{0}")
+ @Parameters(name = "{0}, {1}")
public static List<Object[]> data() {
// TODO(b/141817471): Extend with compilation modes.
return buildParameters(
getTestParameters().withAllRuntimesAndApiLevels().build(),
- KotlinTargetVersion.values(),
- getKotlinCompilers());
+ getKotlinTestParameters().withAllCompilersAndTargetVersions().build());
}
public KotlinInlineFunctionRetraceTest(
- TestParameters parameters, KotlinTargetVersion targetVersion, KotlinCompiler kotlinc) {
- super(targetVersion, kotlinc);
+ TestParameters parameters, KotlinTestParameters kotlinParameters) {
+ super(kotlinParameters);
this.parameters = parameters;
}
diff --git a/src/test/java/com/android/tools/r8/rewrite/assertions/AssertionConfigurationKotlinTest.java b/src/test/java/com/android/tools/r8/rewrite/assertions/AssertionConfigurationKotlinTest.java
index 64ea164..2cc7c96 100644
--- a/src/test/java/com/android/tools/r8/rewrite/assertions/AssertionConfigurationKotlinTest.java
+++ b/src/test/java/com/android/tools/r8/rewrite/assertions/AssertionConfigurationKotlinTest.java
@@ -4,7 +4,6 @@
package com.android.tools.r8.rewrite.assertions;
-import static com.android.tools.r8.ToolHelper.getKotlinCompilers;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
import static org.hamcrest.CoreMatchers.equalTo;
import static org.hamcrest.CoreMatchers.not;
@@ -15,8 +14,8 @@
import com.android.tools.r8.AssertionsConfiguration;
import com.android.tools.r8.D8TestBuilder;
-import com.android.tools.r8.KotlinCompilerTool.KotlinCompiler;
import com.android.tools.r8.KotlinTestBase;
+import com.android.tools.r8.KotlinTestParameters;
import com.android.tools.r8.R8FullTestBuilder;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.ThrowableConsumer;
@@ -63,24 +62,21 @@
private final boolean useJvmAssertions;
private final KotlinCompileMemoizer compiledForAssertions;
- @Parameterized.Parameters(
- name = "{0}, target: {1}, kotlinc: {2}, kotlin-stdlib as library: {3}, -Xassertions=jvm: {4}")
+ @Parameterized.Parameters(name = "{0}, {1}, kotlin-stdlib as library: {2}, -Xassertions=jvm: {3}")
public static Collection<Object[]> data() {
return buildParameters(
getTestParameters().withAllRuntimesAndApiLevels().build(),
- KotlinTargetVersion.values(),
- getKotlinCompilers(),
+ getKotlinTestParameters().withAllCompilersAndTargetVersions().build(),
BooleanUtils.values(),
BooleanUtils.values());
}
public AssertionConfigurationKotlinTest(
TestParameters parameters,
- KotlinTargetVersion targetVersion,
- KotlinCompiler kotlinc,
+ KotlinTestParameters kotlinParameters,
boolean kotlinStdlibAsClasspath,
boolean useJvmAssertions) {
- super(targetVersion, kotlinc);
+ super(kotlinParameters);
this.parameters = parameters;
this.kotlinStdlibAsLibrary = kotlinStdlibAsClasspath;
this.useJvmAssertions = useJvmAssertions;
diff --git a/src/test/java/com/android/tools/r8/shaking/TreeShakingSpecificTest.java b/src/test/java/com/android/tools/r8/shaking/TreeShakingSpecificTest.java
index 5ad87de..7ba46d2 100644
--- a/src/test/java/com/android/tools/r8/shaking/TreeShakingSpecificTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/TreeShakingSpecificTest.java
@@ -5,6 +5,7 @@
import static com.android.tools.r8.ToolHelper.EXAMPLES_BUILD_DIR;
import static com.android.tools.r8.ToolHelper.EXAMPLES_DIR;
+import static org.junit.Assert.assertEquals;
import com.android.tools.r8.CompilationFailedException;
import com.android.tools.r8.Diagnostic;
@@ -26,7 +27,6 @@
import java.util.Collection;
import java.util.stream.Collectors;
import java.util.stream.Stream;
-import org.junit.Assert;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
@@ -92,12 +92,10 @@
new DiagnosticsHandler() {
@Override
public void error(Diagnostic error) {
- String expectedErrorMessage =
- "Compilation can't be completed because the class java.lang.Object is missing.";
- if (error.getDiagnosticMessage().equals(expectedErrorMessage)) {
- return;
- }
- throw new RuntimeException("Unexpected compilation error");
+ assertEquals(
+ "Compilation can't be completed because the following class is missing: "
+ + "java.lang.Object.",
+ error.getDiagnosticMessage());
}
};
R8Command.Builder builder = R8Command.builder(handler)
@@ -146,7 +144,7 @@
"shaking1",
"print-mapping-" + backend.name().toLowerCase() + ".ref")),
StandardCharsets.UTF_8);
- Assert.assertEquals(sorted(refMapping), sorted(actualMapping));
+ assertEquals(sorted(refMapping), sorted(actualMapping));
}
private static String sorted(String str) {
diff --git a/src/test/java/com/android/tools/r8/shaking/annotations/ReflectiveAnnotationUseTest.java b/src/test/java/com/android/tools/r8/shaking/annotations/ReflectiveAnnotationUseTest.java
index bc55c55..8eaee28 100644
--- a/src/test/java/com/android/tools/r8/shaking/annotations/ReflectiveAnnotationUseTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/annotations/ReflectiveAnnotationUseTest.java
@@ -3,7 +3,6 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.shaking.annotations;
-import static com.android.tools.r8.ToolHelper.getKotlinCompilers;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresentAndNotRenamed;
import static org.hamcrest.CoreMatchers.containsString;
@@ -13,10 +12,9 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assume.assumeTrue;
-import com.android.tools.r8.KotlinCompilerTool.KotlinCompiler;
import com.android.tools.r8.KotlinTestBase;
+import com.android.tools.r8.KotlinTestParameters;
import com.android.tools.r8.TestParameters;
-import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
import com.android.tools.r8.graph.DexAnnotationElement;
import com.android.tools.r8.utils.AndroidApp;
import com.android.tools.r8.utils.BooleanUtils;
@@ -62,21 +60,17 @@
private final TestParameters parameters;
private final boolean minify;
- @Parameterized.Parameters(name = "{0}, target: {1}, kotlinc: {2}, minify: {3}")
+ @Parameterized.Parameters(name = "{0}, {1}, minify: {2}")
public static Collection<Object[]> data() {
return buildParameters(
getTestParameters().withAllRuntimesAndApiLevels().build(),
- KotlinTargetVersion.values(),
- getKotlinCompilers(),
+ getKotlinTestParameters().withAllCompilersAndTargetVersions().build(),
BooleanUtils.values());
}
public ReflectiveAnnotationUseTest(
- TestParameters parameters,
- KotlinTargetVersion targetVersion,
- KotlinCompiler kotlinc,
- boolean minify) {
- super(targetVersion, kotlinc);
+ TestParameters parameters, KotlinTestParameters kotlinParameters, boolean minify) {
+ super(kotlinParameters);
this.parameters = parameters;
this.minify = minify;
}
diff --git a/src/test/java/com/android/tools/r8/shaking/b134858535/EventPublisherTest.java b/src/test/java/com/android/tools/r8/shaking/b134858535/EventPublisherTest.java
index 6f9c298..5d5a4b9 100644
--- a/src/test/java/com/android/tools/r8/shaking/b134858535/EventPublisherTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/b134858535/EventPublisherTest.java
@@ -51,8 +51,8 @@
.addKeepClassRules(Interface.class)
.addKeepMainRule(Main.class)
.setMinApi(parameters.getApiLevel())
- .addHorizontallyMergedLambdaClassesInspector(
- inspector -> inspector.assertClassNotMerged(EventPublisher$b.class))
+ .addHorizontallyMergedClassesInspector(
+ inspector -> inspector.assertClassesNotMerged(EventPublisher$b.class))
.compile();
}
diff --git a/src/test/java/com/android/tools/r8/shaking/keptgraph/KeptSingletonIsNotCyclicTest.java b/src/test/java/com/android/tools/r8/shaking/keptgraph/KeptSingletonIsNotCyclicTest.java
index 1a61008..a14683a 100644
--- a/src/test/java/com/android/tools/r8/shaking/keptgraph/KeptSingletonIsNotCyclicTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/keptgraph/KeptSingletonIsNotCyclicTest.java
@@ -18,6 +18,7 @@
import com.android.tools.r8.TestParametersCollection;
import com.android.tools.r8.origin.Origin;
import com.android.tools.r8.references.ClassReference;
+import com.android.tools.r8.references.FieldReference;
import com.android.tools.r8.references.MethodReference;
import com.android.tools.r8.references.Reference;
import com.android.tools.r8.shaking.WhyAreYouKeepingConsumer;
@@ -75,6 +76,7 @@
MethodReference fooClInitRef = Reference.classConstructor(fooClassRef);
MethodReference fooInitRef = Reference.methodFromMethod(fooClass.getDeclaredConstructor());
+ FieldReference testFooFieldRef = Reference.fieldFromField(testClass.getDeclaredField("foo"));
MethodReference testInitRef =
Reference.methodFromMethod(testClass.getDeclaredConstructor());
@@ -96,8 +98,16 @@
QueryNode mainMethod = inspector.method(mainMethodRef).assertNotRenamed().assertKeptBy(root);
// TestClass.<init> is kept by TestClass.main.
QueryNode testInit = inspector.method(testInitRef).assertPresent().assertKeptBy(mainMethod);
- // The type Foo is kept by TestClass.<init>
- QueryNode fooClassNode = inspector.clazz(fooClassRef).assertRenamed().assertKeptBy(testInit);
+ // TestClass.foo is kept by TestClass.<init>.
+ QueryNode testFooFieldNode =
+ inspector.field(testFooFieldRef).assertPresent().assertKeptBy(testInit);
+ // The type Foo is not kept by TestClass.<init>, but TestClass.foo.
+ QueryNode fooClassNode =
+ inspector
+ .clazz(fooClassRef)
+ .assertRenamed()
+ .assertKeptBy(testFooFieldNode)
+ .assertNotKeptBy(testInit);
// Foo.<clinit> is kept by Foo
QueryNode fooClInit = inspector.method(fooClInitRef).assertPresent().assertKeptBy(fooClassNode);
// The type Foo is also kept by Foo.<clinit>
diff --git a/src/test/java/com/android/tools/r8/synthesis/SyntheticItemsTestUtils.java b/src/test/java/com/android/tools/r8/synthesis/SyntheticItemsTestUtils.java
index 3f9c6af..6f3a6cc 100644
--- a/src/test/java/com/android/tools/r8/synthesis/SyntheticItemsTestUtils.java
+++ b/src/test/java/com/android/tools/r8/synthesis/SyntheticItemsTestUtils.java
@@ -86,6 +86,10 @@
return SyntheticNaming.isSynthetic(reference, Phase.EXTERNAL, SyntheticKind.OUTLINE);
}
+ public static boolean isInitializerTypeArgument(ClassReference reference) {
+ return SyntheticNaming.isSynthetic(reference, null, SyntheticKind.INIT_TYPE_ARGUMENT);
+ }
+
public static Matcher<String> containsInternalSyntheticReference() {
return containsString(SyntheticNaming.getPhaseSeparator(Phase.INTERNAL));
}
diff --git a/src/test/java/com/android/tools/r8/tracereferences/TraceReferenceStaticFieldIndirectionTest.java b/src/test/java/com/android/tools/r8/tracereferences/TraceReferenceStaticFieldIndirectionTest.java
new file mode 100644
index 0000000..b0e6069
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/tracereferences/TraceReferenceStaticFieldIndirectionTest.java
@@ -0,0 +1,110 @@
+// Copyright (c) 2021, 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.tracereferences;
+
+import static org.junit.Assert.assertEquals;
+
+import com.android.tools.r8.DiagnosticsChecker;
+import com.android.tools.r8.DiagnosticsHandler;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.references.FieldReference;
+import com.android.tools.r8.references.Reference;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.ZipUtils.ZipBuilder;
+import com.google.common.collect.ImmutableSet;
+import java.nio.file.Path;
+import java.util.HashSet;
+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 TraceReferenceStaticFieldIndirectionTest extends TestBase {
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withNoneRuntime().build();
+ }
+
+ public TraceReferenceStaticFieldIndirectionTest(TestParameters parameters) {}
+
+ static class MissingReferencesConsumer implements TraceReferencesConsumer {
+
+ private Set<FieldReference> seenFields = new HashSet<>();
+
+ @Override
+ public void acceptType(TracedClass tracedClass, DiagnosticsHandler handler) {}
+
+ @Override
+ public void acceptField(TracedField tracedField, DiagnosticsHandler handler) {
+ seenFields.add(tracedField.getReference());
+ }
+
+ @Override
+ public void acceptMethod(TracedMethod tracedMethod, DiagnosticsHandler handler) {}
+ }
+
+ @Test
+ public void traceStaticFields() throws Throwable {
+ Path dir = temp.newFolder().toPath();
+ Path targetJar =
+ ZipBuilder.builder(dir.resolve("target.jar"))
+ .addFilesRelative(
+ ToolHelper.getClassPathForTests(),
+ ToolHelper.getClassFileForTestClass(SuperClass.class),
+ ToolHelper.getClassFileForTestClass(SubClass.class))
+ .build();
+ Path sourceJar =
+ ZipBuilder.builder(dir.resolve("source.jar"))
+ .addFilesRelative(
+ ToolHelper.getClassPathForTests(), ToolHelper.getClassFileForTestClass(Main.class))
+ .build();
+ DiagnosticsChecker diagnosticsChecker = new DiagnosticsChecker();
+ MissingReferencesConsumer consumer = new MissingReferencesConsumer();
+
+ TraceReferences.run(
+ TraceReferencesCommand.builder(diagnosticsChecker)
+ .addLibraryFiles(ToolHelper.getAndroidJar(AndroidApiLevel.P))
+ .addSourceFiles(sourceJar)
+ .addTargetFiles(targetJar)
+ .setConsumer(consumer)
+ .build());
+
+ ImmutableSet<FieldReference> expectedSet =
+ ImmutableSet.of(
+ Reference.field(
+ Reference.classFromClass(SuperClass.class),
+ "field1",
+ Reference.typeFromTypeName("int")),
+ Reference.field(
+ Reference.classFromClass(SubClass.class),
+ "field2",
+ Reference.typeFromTypeName("int")));
+ assertEquals(expectedSet, consumer.seenFields);
+ }
+
+ static class SuperClass {
+
+ static int field1 = 1;
+ }
+
+ static class SubClass extends SuperClass {
+
+ static int field2 = 2;
+ }
+
+ public static class Main {
+
+ public static void main(String[] args) {
+ System.out.println(SubClass.field1);
+ System.out.println(SubClass.field2);
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/tracereferences/TraceReferenceStaticMethodIndirectionTest.java b/src/test/java/com/android/tools/r8/tracereferences/TraceReferenceStaticMethodIndirectionTest.java
new file mode 100644
index 0000000..3c35e76
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/tracereferences/TraceReferenceStaticMethodIndirectionTest.java
@@ -0,0 +1,117 @@
+// Copyright (c) 2021, 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.tracereferences;
+
+import static org.junit.Assert.assertEquals;
+
+import com.android.tools.r8.DiagnosticsChecker;
+import com.android.tools.r8.DiagnosticsHandler;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.references.MethodReference;
+import com.android.tools.r8.references.Reference;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.ZipUtils.ZipBuilder;
+import com.google.common.collect.ImmutableSet;
+import java.nio.file.Path;
+import java.util.Collections;
+import java.util.HashSet;
+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 TraceReferenceStaticMethodIndirectionTest extends TestBase {
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withNoneRuntime().build();
+ }
+
+ public TraceReferenceStaticMethodIndirectionTest(TestParameters parameters) {}
+
+ static class MissingReferencesConsumer implements TraceReferencesConsumer {
+
+ private Set<MethodReference> seenMethods = new HashSet<>();
+
+ @Override
+ public void acceptType(TracedClass tracedClass, DiagnosticsHandler handler) {}
+
+ @Override
+ public void acceptField(TracedField tracedField, DiagnosticsHandler handler) {}
+
+ @Override
+ public void acceptMethod(TracedMethod tracedMethod, DiagnosticsHandler handler) {
+ seenMethods.add(tracedMethod.getReference());
+ }
+ }
+
+ @Test
+ public void traceStaticMethods() throws Throwable {
+ Path dir = temp.newFolder().toPath();
+ Path targetJar =
+ ZipBuilder.builder(dir.resolve("target.jar"))
+ .addFilesRelative(
+ ToolHelper.getClassPathForTests(),
+ ToolHelper.getClassFileForTestClass(SuperClass.class),
+ ToolHelper.getClassFileForTestClass(SubClass.class))
+ .build();
+ Path sourceJar =
+ ZipBuilder.builder(dir.resolve("source.jar"))
+ .addFilesRelative(
+ ToolHelper.getClassPathForTests(), ToolHelper.getClassFileForTestClass(Main.class))
+ .build();
+ DiagnosticsChecker diagnosticsChecker = new DiagnosticsChecker();
+ MissingReferencesConsumer consumer = new MissingReferencesConsumer();
+
+ TraceReferences.run(
+ TraceReferencesCommand.builder(diagnosticsChecker)
+ .addLibraryFiles(ToolHelper.getAndroidJar(AndroidApiLevel.P))
+ .addSourceFiles(sourceJar)
+ .addTargetFiles(targetJar)
+ .setConsumer(consumer)
+ .build());
+
+ ImmutableSet<MethodReference> expectedSet =
+ ImmutableSet.of(
+ Reference.method(
+ Reference.classFromClass(SuperClass.class),
+ "method1",
+ Collections.emptyList(),
+ null),
+ Reference.method(
+ Reference.classFromClass(SubClass.class),
+ "method2",
+ Collections.emptyList(),
+ null));
+ assertEquals(expectedSet, consumer.seenMethods);
+ }
+
+ static class SuperClass {
+
+ static void method1() {
+ System.out.println("SuperClass::method1");
+ }
+ }
+
+ static class SubClass extends SuperClass {
+
+ static void method2() {
+ System.out.println("SuperClass::method2");
+ }
+ }
+
+ public static class Main {
+
+ public static void main(String[] args) {
+ SuperClass.method1();
+ SubClass.method2();
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/AbsentClassSubject.java b/src/test/java/com/android/tools/r8/utils/codeinspector/AbsentClassSubject.java
index cb09a17..491bf9d 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/AbsentClassSubject.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/AbsentClassSubject.java
@@ -12,6 +12,7 @@
import com.android.tools.r8.references.ClassReference;
import java.util.List;
import java.util.function.Consumer;
+import java.util.function.Predicate;
import kotlinx.metadata.jvm.KotlinClassMetadata;
import org.junit.rules.TemporaryFolder;
@@ -38,7 +39,7 @@
}
@Override
- public MethodSubject uniqueInstanceInitializer() {
+ public MethodSubject uniqueMethodThatMatches(Predicate<FoundMethodSubject> predicate) {
return new AbsentMethodSubject();
}
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/AssertUtils.java b/src/test/java/com/android/tools/r8/utils/codeinspector/AssertUtils.java
index e9634a5..174e734 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/AssertUtils.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/AssertUtils.java
@@ -9,6 +9,8 @@
import com.android.tools.r8.CompilationFailedException;
import com.android.tools.r8.utils.ThrowingAction;
+import java.io.ByteArrayOutputStream;
+import java.io.PrintStream;
import java.util.function.Consumer;
public class AssertUtils {
@@ -54,7 +56,7 @@
action.execute();
fail("Expected action to fail with an exception, but succeeded");
} catch (Throwable e) {
- assertEquals(clazz, e.getClass());
+ assertEquals(printStackTraceToString(e), clazz, e.getClass());
if (consumer != null) {
consumer.accept(e);
}
@@ -63,4 +65,12 @@
action.execute();
}
}
+
+ private static String printStackTraceToString(Throwable e) {
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ try (PrintStream ps = new PrintStream(baos)) {
+ e.printStackTrace(ps);
+ }
+ return baos.toString();
+ }
}
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/ClassSubject.java b/src/test/java/com/android/tools/r8/utils/codeinspector/ClassSubject.java
index cec4e2b..ee96d6b 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/ClassSubject.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/ClassSubject.java
@@ -96,7 +96,11 @@
public abstract MethodSubject method(String returnType, String name, List<String> parameters);
- public abstract MethodSubject uniqueInstanceInitializer();
+ public final MethodSubject uniqueInstanceInitializer() {
+ return uniqueMethodThatMatches(FoundMethodSubject::isInstanceInitializer);
+ }
+
+ public abstract MethodSubject uniqueMethodThatMatches(Predicate<FoundMethodSubject> predicate);
public abstract MethodSubject uniqueMethodWithName(String name);
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/FoundClassSubject.java b/src/test/java/com/android/tools/r8/utils/codeinspector/FoundClassSubject.java
index 0bfe560..ed04f35 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/FoundClassSubject.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/FoundClassSubject.java
@@ -46,6 +46,7 @@
import java.util.List;
import java.util.Set;
import java.util.function.Consumer;
+import java.util.function.Predicate;
import kotlinx.metadata.jvm.KotlinClassMetadata;
import org.junit.rules.TemporaryFolder;
@@ -128,9 +129,9 @@
}
@Override
- public MethodSubject uniqueInstanceInitializer() {
+ public MethodSubject uniqueMethodThatMatches(Predicate<FoundMethodSubject> predicate) {
MethodSubject methodSubject = null;
- for (FoundMethodSubject candidate : allMethods(FoundMethodSubject::isInstanceInitializer)) {
+ for (FoundMethodSubject candidate : allMethods(predicate)) {
assert methodSubject == null;
methodSubject = candidate;
}
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/HorizontallyMergedClassesInspector.java b/src/test/java/com/android/tools/r8/utils/codeinspector/HorizontallyMergedClassesInspector.java
index e90df17..a5c623c 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/HorizontallyMergedClassesInspector.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/HorizontallyMergedClassesInspector.java
@@ -8,16 +8,25 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+import com.android.tools.r8.TestBase;
import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.horizontalclassmerging.HorizontallyMergedClasses;
+import com.android.tools.r8.references.ClassReference;
+import com.android.tools.r8.references.Reference;
import com.android.tools.r8.utils.SetUtils;
+import com.android.tools.r8.utils.StringUtils;
import com.google.common.collect.Sets;
+import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
+import java.util.List;
import java.util.Set;
import java.util.function.BiConsumer;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
public class HorizontallyMergedClassesInspector {
@@ -59,8 +68,32 @@
public HorizontallyMergedClassesInspector assertMergedInto(Class<?> from, Class<?> target) {
assertEquals(
- horizontallyMergedClasses.getMergeTargetOrDefault(toDexType(from, dexItemFactory)),
- toDexType(target, dexItemFactory));
+ horizontallyMergedClasses.getMergeTargetOrDefault(toDexType(from)), toDexType(target));
+ return this;
+ }
+
+ public HorizontallyMergedClassesInspector assertClassesMerged(Collection<Class<?>> classes) {
+ return assertTypesMerged(classes.stream().map(this::toDexType).collect(Collectors.toList()));
+ }
+
+ public HorizontallyMergedClassesInspector assertClassReferencesMerged(
+ Collection<ClassReference> classReferences) {
+ return assertTypesMerged(
+ classReferences.stream().map(this::toDexType).collect(Collectors.toList()));
+ }
+
+ public HorizontallyMergedClassesInspector assertTypesMerged(Collection<DexType> types) {
+ List<DexType> unmerged = new ArrayList<>();
+ for (DexType type : types) {
+ if (!horizontallyMergedClasses.hasBeenMergedOrIsMergeTarget(type)) {
+ unmerged.add(type);
+ }
+ }
+ assertEquals(
+ "Expected the following classes to be merged: "
+ + StringUtils.join(", ", unmerged, DexType::getTypeName),
+ 0,
+ unmerged.size());
return this;
}
@@ -69,52 +102,91 @@
return this;
}
- public HorizontallyMergedClassesInspector assertClassNotMerged(Class<?> clazz) {
- assertFalse(
- horizontallyMergedClasses.hasBeenMergedIntoDifferentType(toDexType(clazz, dexItemFactory)));
- return this;
- }
-
- public HorizontallyMergedClassesInspector assertClassesNotMerged(Collection<Class<?>> classes) {
- for (Class<?> clazz : classes) {
- assertClassNotMerged(clazz);
- }
- return this;
- }
-
public HorizontallyMergedClassesInspector assertClassesNotMerged(Class<?>... classes) {
return assertClassesNotMerged(Arrays.asList(classes));
}
- public HorizontallyMergedClassesInspector assertClassNotMergedIntoDifferentType(Class<?> clazz) {
- assertFalse(
- horizontallyMergedClasses.hasBeenMergedIntoDifferentType(toDexType(clazz, dexItemFactory)));
- return this;
+ public HorizontallyMergedClassesInspector assertClassesNotMerged(Collection<Class<?>> classes) {
+ return assertTypesNotMerged(classes.stream().map(this::toDexType).collect(Collectors.toList()));
}
- public HorizontallyMergedClassesInspector assertMerged(Class<?> clazz) {
- assertTrue(
- horizontallyMergedClasses.hasBeenMergedOrIsMergeTarget(toDexType(clazz, dexItemFactory)));
- return this;
+ public HorizontallyMergedClassesInspector assertClassReferencesNotMerged(
+ ClassReference... classReferences) {
+ return assertClassReferencesNotMerged(Arrays.asList(classReferences));
}
- public HorizontallyMergedClassesInspector assertMerged(Class<?>... classes) {
- for (Class<?> clazz : classes) {
- assertMerged(clazz);
+ public HorizontallyMergedClassesInspector assertClassReferencesNotMerged(
+ Collection<ClassReference> classReferences) {
+ return assertTypesNotMerged(
+ classReferences.stream().map(this::toDexType).collect(Collectors.toList()));
+ }
+
+ public HorizontallyMergedClassesInspector assertTypesNotMerged(DexType... types) {
+ return assertTypesNotMerged(Arrays.asList(types));
+ }
+
+ public HorizontallyMergedClassesInspector assertTypesNotMerged(Collection<DexType> types) {
+ for (DexType type : types) {
+ assertTrue(type.isClassType());
+ assertFalse(horizontallyMergedClasses.hasBeenMergedOrIsMergeTarget(type));
}
return this;
}
- public HorizontallyMergedClassesInspector assertMergedIntoDifferentType(Class<?> clazz) {
- assertTrue(
- horizontallyMergedClasses.hasBeenMergedIntoDifferentType(toDexType(clazz, dexItemFactory)));
+ public HorizontallyMergedClassesInspector assertIsCompleteMergeGroup(String... typeNames) {
+ return assertIsCompleteMergeGroup(
+ Stream.of(typeNames).map(Reference::classFromTypeName).collect(Collectors.toList()));
+ }
+
+ public HorizontallyMergedClassesInspector assertIsCompleteMergeGroup(
+ Collection<ClassReference> classReferences) {
+ assertFalse(classReferences.isEmpty());
+ List<DexType> types =
+ classReferences.stream().map(this::toDexType).collect(Collectors.toList());
+ DexType uniqueTarget = null;
+ for (DexType type : types) {
+ if (horizontallyMergedClasses.isMergeTarget(type)) {
+ if (uniqueTarget == null) {
+ uniqueTarget = type;
+ } else {
+ fail(
+ "Expected a single merge target, but found "
+ + type.getTypeName()
+ + " and "
+ + uniqueTarget.getTypeName());
+ }
+ }
+ }
+ if (uniqueTarget == null) {
+ for (DexType type : types) {
+ if (horizontallyMergedClasses.hasBeenMergedIntoDifferentType(type)) {
+ fail(
+ "Expected merge target "
+ + horizontallyMergedClasses.getMergeTargetOrDefault(type).getTypeName()
+ + " to be in merge group");
+ }
+ }
+ fail("Expected to find a merge target, but none found");
+ }
+ Set<DexType> sources = horizontallyMergedClasses.getSourcesFor(uniqueTarget);
+ assertEquals(
+ "Expected to find "
+ + (classReferences.size() - 1)
+ + " source(s) for merge target "
+ + uniqueTarget.getTypeName()
+ + ", but only found: "
+ + StringUtils.join(", ", sources, DexType::getTypeName),
+ classReferences.size() - 1,
+ sources.size());
+ assertTrue(types.containsAll(sources));
return this;
}
- public HorizontallyMergedClassesInspector assertMergedIntoDifferentType(Class<?>... classes) {
- for (Class<?> clazz : classes) {
- assertMergedIntoDifferentType(clazz);
- }
- return this;
+ private DexType toDexType(Class<?> clazz) {
+ return TestBase.toDexType(clazz, dexItemFactory);
+ }
+
+ private DexType toDexType(ClassReference classReference) {
+ return TestBase.toDexType(classReference, dexItemFactory);
}
}
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/HorizontallyMergedLambdaClassesInspector.java b/src/test/java/com/android/tools/r8/utils/codeinspector/HorizontallyMergedLambdaClassesInspector.java
deleted file mode 100644
index 619b415..0000000
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/HorizontallyMergedLambdaClassesInspector.java
+++ /dev/null
@@ -1,53 +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.utils.codeinspector;
-
-import static com.android.tools.r8.TestBase.toDexType;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
-
-import com.android.tools.r8.graph.DexItemFactory;
-import com.android.tools.r8.graph.DexType;
-import com.android.tools.r8.graph.classmerging.HorizontallyMergedLambdaClasses;
-import java.util.Set;
-import java.util.function.BiConsumer;
-
-public class HorizontallyMergedLambdaClassesInspector {
-
- private final DexItemFactory dexItemFactory;
- private final HorizontallyMergedLambdaClasses horizontallyMergedLambdaClasses;
-
- public HorizontallyMergedLambdaClassesInspector(
- DexItemFactory dexItemFactory,
- HorizontallyMergedLambdaClasses horizontallyMergedLambdaClasses) {
- this.dexItemFactory = dexItemFactory;
- this.horizontallyMergedLambdaClasses = horizontallyMergedLambdaClasses;
- }
-
- public HorizontallyMergedLambdaClassesInspector assertMerged(Class<?> clazz) {
- assertTrue(
- horizontallyMergedLambdaClasses.hasBeenMergedIntoDifferentType(
- toDexType(clazz, dexItemFactory)));
- return this;
- }
-
- public HorizontallyMergedLambdaClassesInspector assertMerged(Class<?>... classes) {
- for (Class<?> clazz : classes) {
- assertMerged(clazz);
- }
- return this;
- }
-
- public HorizontallyMergedLambdaClassesInspector assertClassNotMerged(Class<?> clazz) {
- assertFalse(
- horizontallyMergedLambdaClasses.hasBeenMergedIntoDifferentType(
- toDexType(clazz, dexItemFactory)));
- return this;
- }
-
- public void forEachMergeGroup(BiConsumer<Set<DexType>, DexType> consumer) {
- horizontallyMergedLambdaClasses.forEachMergeGroup(consumer);
- }
-}