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); - } -}