Merge commit '62b3a4af8a0017ca8ebd236845894af2b462bdf0' into dev-release
diff --git a/.gitignore b/.gitignore
index a5b9426..147cf9a 100644
--- a/.gitignore
+++ b/.gitignore
@@ -217,6 +217,7 @@
third_party/youtube-developer/20200415.tar.gz
tmp/
tools/*.pyc
+tools/__pycache__
tools/*/art
tools/*/art-5.1.1
tools/*/art-5.1.1.tar.gz
diff --git a/src/main/java/com/android/tools/r8/D8.java b/src/main/java/com/android/tools/r8/D8.java
index 4def5d0..b762557 100644
--- a/src/main/java/com/android/tools/r8/D8.java
+++ b/src/main/java/com/android/tools/r8/D8.java
@@ -271,6 +271,9 @@
if (result != null) {
appView.setAppInfo(new AppInfo(result.commit, appView.appInfo().getMainDexClasses()));
appView.pruneItems(result.prunedItems);
+ if (result.lens != null) {
+ appView.setGraphLens(result.lens);
+ }
}
new CfApplicationWriter(
appView,
@@ -315,6 +318,9 @@
if (result != null) {
appView.setAppInfo(new AppInfo(result.commit, appView.appInfo().getMainDexClasses()));
appView.pruneItems(result.prunedItems);
+ if (result.lens != null) {
+ appView.setGraphLens(result.lens);
+ }
}
new ApplicationWriter(
diff --git a/src/main/java/com/android/tools/r8/D8Command.java b/src/main/java/com/android/tools/r8/D8Command.java
index 047df78..b12a940 100644
--- a/src/main/java/com/android/tools/r8/D8Command.java
+++ b/src/main/java/com/android/tools/r8/D8Command.java
@@ -445,6 +445,11 @@
internal.programConsumer = getProgramConsumer();
if (internal.isGeneratingClassFiles()) {
internal.cfToCfDesugar = true;
+ // Turn off switch optimizations when desugaring to class file format.
+ assert internal.enableSwitchRewriting;
+ internal.enableSwitchRewriting = false;
+ assert internal.enableStringSwitchConversion;
+ internal.enableStringSwitchConversion = false;
}
internal.mainDexListConsumer = getMainDexListConsumer();
internal.minimalMainDex = internal.debug || minimalMainDex;
diff --git a/src/main/java/com/android/tools/r8/GenerateMainDexList.java b/src/main/java/com/android/tools/r8/GenerateMainDexList.java
index d260bcb..bf85148 100644
--- a/src/main/java/com/android/tools/r8/GenerateMainDexList.java
+++ b/src/main/java/com/android/tools/r8/GenerateMainDexList.java
@@ -96,7 +96,8 @@
}
Enqueuer enqueuer =
- EnqueuerFactory.createForMainDexTracing(appView, subtypingInfo, graphConsumer);
+ 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();
diff --git a/src/main/java/com/android/tools/r8/L8.java b/src/main/java/com/android/tools/r8/L8.java
index f610d83..64fc53e 100644
--- a/src/main/java/com/android/tools/r8/L8.java
+++ b/src/main/java/com/android/tools/r8/L8.java
@@ -94,9 +94,19 @@
ExceptionUtils.withD8CompilationHandler(
options.reporter,
() -> {
+ // Desugar to class file format and turn off switch optimizations, as the final
+ // compilation with D8 or R8 will do that.
options.cfToCfDesugar = true;
+ assert options.enableSwitchRewriting;
+ options.enableSwitchRewriting = false;
+ assert options.enableStringSwitchConversion;
+ options.enableStringSwitchConversion = false;
+
desugar(app, options, executorService);
+
options.cfToCfDesugar = false;
+ options.enableSwitchRewriting = true;
+ options.enableStringSwitchConversion = true;
});
assert !options.cfToCfDesugar;
if (shrink) {
@@ -133,6 +143,9 @@
if (result != null) {
appView.setAppInfo(new AppInfo(result.commit, appView.appInfo().getMainDexClasses()));
appView.pruneItems(result.prunedItems);
+ if (result.lens != null) {
+ appView.setGraphLens(result.lens);
+ }
}
NamingLens namingLens = PrefixRewritingNamingLens.createPrefixRewritingNamingLens(appView);
diff --git a/src/main/java/com/android/tools/r8/PrintSeeds.java b/src/main/java/com/android/tools/r8/PrintSeeds.java
index 212eb32..4be995a 100644
--- a/src/main/java/com/android/tools/r8/PrintSeeds.java
+++ b/src/main/java/com/android/tools/r8/PrintSeeds.java
@@ -92,10 +92,9 @@
RootSet rootSet =
new RootSetBuilder(appView, subtypingInfo, options.getProguardConfiguration().getRules())
.run(executor);
- Enqueuer enqueuer = EnqueuerFactory.createForInitialTreeShaking(appView, subtypingInfo);
- AppInfoWithLiveness appInfo =
- enqueuer.traceApplication(
- rootSet, options.getProguardConfiguration().getDontWarnPatterns(), executor, timing);
+ Enqueuer enqueuer =
+ EnqueuerFactory.createForInitialTreeShaking(appView, executor, subtypingInfo);
+ AppInfoWithLiveness appInfo = enqueuer.traceApplication(rootSet, executor, timing);
RootSetBuilder.writeSeeds(
appInfo, System.out, type -> descriptors.contains(type.toDescriptorString()));
} catch (ExecutionException e) {
diff --git a/src/main/java/com/android/tools/r8/R8.java b/src/main/java/com/android/tools/r8/R8.java
index 9e7a967..5e31835 100644
--- a/src/main/java/com/android/tools/r8/R8.java
+++ b/src/main/java/com/android/tools/r8/R8.java
@@ -31,7 +31,6 @@
import com.android.tools.r8.graph.DexReference;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.DirectMappedDexApplication;
-import com.android.tools.r8.graph.DirectMappedDexApplication.Builder;
import com.android.tools.r8.graph.GraphLens;
import com.android.tools.r8.graph.GraphLens.NestedGraphLens;
import com.android.tools.r8.graph.InitClassLens;
@@ -49,8 +48,6 @@
import com.android.tools.r8.ir.desugar.BackportedMethodRewriter;
import com.android.tools.r8.ir.desugar.DesugaredLibraryRetargeter;
import com.android.tools.r8.ir.desugar.InterfaceMethodRewriter;
-import com.android.tools.r8.ir.desugar.NestedPrivateMethodLens;
-import com.android.tools.r8.ir.desugar.R8NestBasedAccessDesugaring;
import com.android.tools.r8.ir.optimize.AssertionsRewriter;
import com.android.tools.r8.ir.optimize.MethodPoolCollection;
import com.android.tools.r8.ir.optimize.NestReducer;
@@ -332,7 +329,7 @@
.rebuildWithClassHierarchy(
MissingClasses.builderForInitialMissingClasses()
.addNewMissingClasses(new SubtypingInfo(appView).getMissingClasses())
- .reportMissingClasses(options)));
+ .reportMissingClasses(appView)));
}
// Add synthesized -assumenosideeffects from min api if relevant.
@@ -420,6 +417,8 @@
}
assert appView.appInfo().hasLiveness();
+ AppView<AppInfoWithLiveness> appViewWithLiveness = appView.withLiveness();
+
assert verifyNoJarApplicationReaders(appView.appInfo().classes());
// 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
@@ -435,7 +434,7 @@
.run(executorService);
// Live types is the tracing result.
Set<DexProgramClass> mainDexBaseClasses =
- EnqueuerFactory.createForMainDexTracing(appView, subtypingInfo)
+ 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();
@@ -448,7 +447,6 @@
appView.dexItemFactory().clearTypeElementsCache();
if (options.getProguardConfiguration().isAccessModificationAllowed()) {
- AppView<AppInfoWithLiveness> appViewWithLiveness = appView.withLiveness();
SubtypingInfo subtypingInfo = appViewWithLiveness.appInfo().computeSubtypingInfo();
GraphLens publicizedLens =
ClassAndMemberPublicizer.run(
@@ -466,29 +464,13 @@
}
}
- AppView<AppInfoWithLiveness> appViewWithLiveness = appView.withLiveness();
+ // This pass attempts to reduce the number of nests and nest size to allow further passes, and
+ // should therefore be run after the publicizer.
+ new NestReducer(appViewWithLiveness).run(executorService, timing);
+
appView.setGraphLens(new MemberRebindingAnalysis(appViewWithLiveness).run(executorService));
appView.appInfo().withLiveness().getFieldAccessInfoCollection().restrictToProgram(appView);
- if (options.shouldDesugarNests()) {
- timing.begin("NestBasedAccessDesugaring");
- R8NestBasedAccessDesugaring analyzer = new R8NestBasedAccessDesugaring(appViewWithLiveness);
- Builder appBuilder = getDirectApp(appView).builder();
- NestedPrivateMethodLens lens = analyzer.run(executorService, appBuilder);
- if (lens != null) {
- appView.rewriteWithLensAndApplication(lens, appBuilder.build());
- }
- timing.end();
- } else {
- timing.begin("NestReduction");
- // This pass attempts to reduce the number of nests and nest size
- // to allow further passes, specifically the class mergers, to do
- // a better job. This pass is better run before the class merger
- // but after the publicizer (cannot be run as part of the Enqueuer).
- new NestReducer(appViewWithLiveness).run(executorService);
- timing.end();
- }
-
boolean isKotlinLibraryCompilationWithInlinePassThrough =
options.enableCfByteCodePassThrough && appView.hasCfByteCodePassThroughMethods();
@@ -644,8 +626,12 @@
}
Enqueuer enqueuer =
- EnqueuerFactory.createForMainDexTracing(
- appView, new SubtypingInfo(appView), mainDexKeptGraphConsumer);
+ 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 =
@@ -694,13 +680,13 @@
Enqueuer enqueuer =
EnqueuerFactory.createForFinalTreeShaking(
appView,
+ executorService,
new SubtypingInfo(appView),
keptGraphConsumer,
prunedTypes);
appView.setAppInfo(
enqueuer.traceApplication(
appView.rootSet(),
- options.getProguardConfiguration().getDontWarnPatterns(),
executorService,
timing));
// Rerunning the enqueuer should not give rise to any method rewritings.
@@ -827,7 +813,9 @@
lens, appBuilder.build(), memberRebindingLens.getPrevious());
}
}
- assert Repackaging.verifyIdentityRepackaging(appView);
+ if (appView.appInfo().hasLiveness()) {
+ assert Repackaging.verifyIdentityRepackaging(appView.withLiveness());
+ }
// Add automatic main dex classes to an eventual manual list of classes.
if (!options.mainDexKeepRules.isEmpty()) {
@@ -838,12 +826,21 @@
appView.getSyntheticItems().computeFinalSynthetics(appView);
if (result != null) {
if (appView.appInfo().hasLiveness()) {
- appViewWithLiveness.setAppInfo(
- appViewWithLiveness.appInfo().rebuildWithLiveness(result.commit));
+ if (result.lens == null) {
+ appViewWithLiveness.setAppInfo(
+ appViewWithLiveness.appInfo().rebuildWithLiveness(result.commit));
+ } else {
+ appViewWithLiveness.rewriteWithLensAndApplication(
+ result.lens, result.commit.getApplication().asDirect());
+ }
+ appViewWithLiveness.pruneItems(result.prunedItems);
} else {
appView.setAppInfo(appView.appInfo().rebuildWithClassHierarchy(result.commit));
+ appView.pruneItems(result.prunedItems);
+ if (result.lens != null) {
+ appView.setGraphLens(result.lens);
+ }
}
- appViewWithLiveness.pruneItems(result.prunedItems);
}
// Perform minification.
@@ -931,6 +928,8 @@
options,
ProguardMapSupplier.create(classNameMapper, options));
+ assert appView.getDontWarnConfiguration().validate(options);
+
options.printWarnings();
} catch (ExecutionException e) {
throw unwrapExecutionException(e);
@@ -1002,7 +1001,8 @@
SubtypingInfo subtypingInfo,
RuntimeTypeCheckInfo.Builder classMergingEnqueuerExtensionBuilder)
throws ExecutionException {
- Enqueuer enqueuer = EnqueuerFactory.createForInitialTreeShaking(appView, subtypingInfo);
+ Enqueuer enqueuer =
+ EnqueuerFactory.createForInitialTreeShaking(appView, executorService, subtypingInfo);
enqueuer.setAnnotationRemoverBuilder(annotationRemoverBuilder);
if (appView.options().enableInitializedClassesInInstanceMethodsAnalysis) {
enqueuer.registerAnalysis(new InitializedClassesInInstanceMethodsAnalysis(appView));
@@ -1021,7 +1021,6 @@
appView.setAppInfo(
enqueuer.traceApplication(
appView.rootSet(),
- options.getProguardConfiguration().getDontWarnPatterns(),
executorService,
timing));
NestedGraphLens lens = enqueuer.buildGraphLens();
@@ -1071,16 +1070,19 @@
SubtypingInfo subtypingInfo = new SubtypingInfo(appView);
if (forMainDex) {
enqueuer =
- EnqueuerFactory.createForMainDexTracing(
- appView, subtypingInfo, whyAreYouKeepingConsumer);
+ EnqueuerFactory.createForFinalMainDexTracing(
+ appView,
+ executorService,
+ subtypingInfo,
+ whyAreYouKeepingConsumer,
+ MainDexTracingResult.NONE);
enqueuer.traceMainDex(rootSet, executorService, timing);
} else {
enqueuer =
EnqueuerFactory.createForWhyAreYouKeeping(
- appView, subtypingInfo, whyAreYouKeepingConsumer);
+ appView, executorService, subtypingInfo, whyAreYouKeepingConsumer);
enqueuer.traceApplication(
rootSet,
- options.getProguardConfiguration().getDontWarnPatterns(),
executorService,
timing);
}
diff --git a/src/main/java/com/android/tools/r8/ReadProguardMap.java b/src/main/java/com/android/tools/r8/ReadProguardMap.java
index 31bc2d8..0e02ce0 100644
--- a/src/main/java/com/android/tools/r8/ReadProguardMap.java
+++ b/src/main/java/com/android/tools/r8/ReadProguardMap.java
@@ -38,7 +38,7 @@
if (args.length == 0) {
readProguardMapFile(DEFAULT_MAP_FILE_NAME);
} else {
- Arrays.asList(args).forEach((String name) -> readProguardMapFile(name));
+ Arrays.asList(args).forEach(this::readProguardMapFile);
}
timing.report();
}
diff --git a/src/main/java/com/android/tools/r8/cf/CfCodePrinter.java b/src/main/java/com/android/tools/r8/cf/CfCodePrinter.java
index b4b0d5a..d56b873 100644
--- a/src/main/java/com/android/tools/r8/cf/CfCodePrinter.java
+++ b/src/main/java/com/android/tools/r8/cf/CfCodePrinter.java
@@ -452,9 +452,9 @@
@Override
public void print(CfFrame frame) {
- String keys = join(frame.getLocals().keySet(), ",", BraceType.NONE);
- String values = join(frame.getLocals().values(), ",", BraceType.NONE, this::frameTypeType);
- String stack = join(frame.getStack(), ",", BraceType.NONE, this::frameTypeType);
+ String keys = join(frame.getLocals().keySet(), ",");
+ String values = join(",", frame.getLocals().values(), this::frameTypeType);
+ String stack = join(",", frame.getStack(), this::frameTypeType);
printNewInstruction(
"CfFrame",
"new "
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfFieldInstruction.java b/src/main/java/com/android/tools/r8/cf/code/CfFieldInstruction.java
index 6176b49..be6176b 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfFieldInstruction.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfFieldInstruction.java
@@ -65,6 +65,10 @@
return visitor.visit(this, other.asFieldInstruction(), CfFieldInstruction::specify);
}
+ public boolean isFieldGet() {
+ return opcode == Opcodes.GETFIELD || opcode == Opcodes.GETSTATIC;
+ }
+
@Override
public CfFieldInstruction asFieldInstruction() {
return this;
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfInvoke.java b/src/main/java/com/android/tools/r8/cf/code/CfInvoke.java
index 6c7f76f..00d4c93 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfInvoke.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfInvoke.java
@@ -170,6 +170,10 @@
!method.name.toString().equals(Constants.INSTANCE_INITIALIZER_NAME);
}
+ public boolean isInvokeStatic() {
+ return opcode == Opcodes.INVOKESTATIC;
+ }
+
public boolean isInvokeVirtual() {
return opcode == Opcodes.INVOKEVIRTUAL;
}
diff --git a/src/main/java/com/android/tools/r8/code/Instruction.java b/src/main/java/com/android/tools/r8/code/Instruction.java
index 977dafd..ccfd713 100644
--- a/src/main/java/com/android/tools/r8/code/Instruction.java
+++ b/src/main/java/com/android/tools/r8/code/Instruction.java
@@ -332,6 +332,9 @@
@Override
public final void acceptHashing(HashingVisitor visitor) {
// Rather than traverse the full instruction, the compare ID will likely give a reasonable hash.
+ // TODO(b/158159959): This will likely lead to a lot of distinct synthetics hashing to the same
+ // hash as many have the same instruction pattern such as an invoke of the impl method or a
+ // field access.
visitor.visitInt(getCompareToId());
}
diff --git a/src/main/java/com/android/tools/r8/dex/FileWriter.java b/src/main/java/com/android/tools/r8/dex/FileWriter.java
index ef6b7cd..bbe4a90 100644
--- a/src/main/java/com/android/tools/r8/dex/FileWriter.java
+++ b/src/main/java/com/android/tools/r8/dex/FileWriter.java
@@ -55,7 +55,7 @@
import com.android.tools.r8.naming.MemberNaming.Signature;
import com.android.tools.r8.naming.NamingLens;
import com.android.tools.r8.position.MethodPosition;
-import com.android.tools.r8.synthesis.SyntheticItems;
+import com.android.tools.r8.synthesis.SyntheticNaming;
import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.DexVersion;
import com.android.tools.r8.utils.InternalOptions;
@@ -323,7 +323,7 @@
for (DexType type : mapping.getTypes()) {
if (type.isClassType()) {
assert DexString.isValidSimpleName(apiLevel, type.getName());
- assert SyntheticItems.verifyNotInternalSynthetic(type);
+ assert SyntheticNaming.verifyNotInternalSynthetic(type);
}
}
diff --git a/src/main/java/com/android/tools/r8/dex/InheritanceClassInDexDistributor.java b/src/main/java/com/android/tools/r8/dex/InheritanceClassInDexDistributor.java
index 8e8750a..1183590 100644
--- a/src/main/java/com/android/tools/r8/dex/InheritanceClassInDexDistributor.java
+++ b/src/main/java/com/android/tools/r8/dex/InheritanceClassInDexDistributor.java
@@ -548,7 +548,7 @@
private void updateGroupsNumberOfIds(List<ClassGroup> groups) {
Collection<Future<?>> updateIdsTasks = new ArrayList<>(groups.size());
for (ClassGroup group : groups) {
- updateIdsTasks.add(executorService.submit(() -> group.updateNumbersOfIds()));
+ updateIdsTasks.add(executorService.submit(group::updateNumbersOfIds));
}
try {
ThreadUtils.awaitFutures(updateIdsTasks);
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 917f0de..7da0bc5 100644
--- a/src/main/java/com/android/tools/r8/dex/VirtualFile.java
+++ b/src/main/java/com/android/tools/r8/dex/VirtualFile.java
@@ -28,7 +28,7 @@
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.synthesis.SyntheticItems;
+import com.android.tools.r8.synthesis.SyntheticNaming;
import com.android.tools.r8.utils.DescriptorUtils;
import com.android.tools.r8.utils.FileUtils;
import com.android.tools.r8.utils.InternalOptions;
@@ -667,7 +667,7 @@
@Override
public boolean addType(DexType type) {
- assert SyntheticItems.verifyNotInternalSynthetic(type);
+ assert SyntheticNaming.verifyNotInternalSynthetic(type);
return types.add(type);
}
@@ -790,7 +790,7 @@
@Override
public boolean addType(DexType type) {
- assert SyntheticItems.verifyNotInternalSynthetic(type);
+ assert SyntheticNaming.verifyNotInternalSynthetic(type);
return maybeInsert(type, types, base.types);
}
diff --git a/src/main/java/com/android/tools/r8/errors/dontwarn/DontWarnConfiguration.java b/src/main/java/com/android/tools/r8/errors/dontwarn/DontWarnConfiguration.java
new file mode 100644
index 0000000..ff4a73d
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/errors/dontwarn/DontWarnConfiguration.java
@@ -0,0 +1,43 @@
+// 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.errors.dontwarn;
+
+import com.android.tools.r8.graph.DexClass;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.shaking.ProguardConfiguration;
+import com.android.tools.r8.utils.InternalOptions;
+import java.util.Set;
+
+public abstract class DontWarnConfiguration {
+
+ public static DontWarnConfiguration create(ProguardConfiguration proguardConfiguration) {
+ if (proguardConfiguration != null && proguardConfiguration.hasDontWarnPatterns()) {
+ return new NonEmptyDontWarnConfiguration(
+ proguardConfiguration.getDontWarnPatterns(new Witness()));
+ }
+ return empty();
+ }
+
+ public static EmptyDontWarnConfiguration empty() {
+ return new EmptyDontWarnConfiguration();
+ }
+
+ public abstract Set<DexType> getNonMatches(Set<DexType> types);
+
+ public final boolean matches(DexClass clazz) {
+ return matches(clazz.getType());
+ }
+
+ public abstract boolean matches(DexType type);
+
+ public abstract boolean validate(InternalOptions options);
+
+ // Witness that can only be instantiated by the DontWarnConfiguration, to ensure that the dont
+ // warn patterns are only accessed via the DontWarnConfiguration.
+ public static class Witness {
+
+ private Witness() {}
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/errors/dontwarn/EmptyDontWarnConfiguration.java b/src/main/java/com/android/tools/r8/errors/dontwarn/EmptyDontWarnConfiguration.java
new file mode 100644
index 0000000..e115168
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/errors/dontwarn/EmptyDontWarnConfiguration.java
@@ -0,0 +1,27 @@
+// 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.errors.dontwarn;
+
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.utils.InternalOptions;
+import java.util.Set;
+
+public class EmptyDontWarnConfiguration extends DontWarnConfiguration {
+
+ @Override
+ public Set<DexType> getNonMatches(Set<DexType> types) {
+ return types;
+ }
+
+ @Override
+ public boolean matches(DexType type) {
+ return false;
+ }
+
+ @Override
+ public boolean validate(InternalOptions options) {
+ return true;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/errors/dontwarn/NonEmptyDontWarnConfiguration.java b/src/main/java/com/android/tools/r8/errors/dontwarn/NonEmptyDontWarnConfiguration.java
new file mode 100644
index 0000000..e96cb83
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/errors/dontwarn/NonEmptyDontWarnConfiguration.java
@@ -0,0 +1,89 @@
+// 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.errors.dontwarn;
+
+import static java.util.Collections.emptySet;
+
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.shaking.ProguardClassFilter;
+import com.android.tools.r8.shaking.ProguardClassNameList;
+import com.android.tools.r8.utils.InternalOptions;
+import com.google.common.collect.Sets;
+import java.util.IdentityHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+public class NonEmptyDontWarnConfiguration extends DontWarnConfiguration {
+
+ private final List<ProguardClassNameList> dontWarnPatterns;
+ private final Map<ProguardClassNameList, Set<DexType>> matchedDontWarnPatterns =
+ new IdentityHashMap<>();
+
+ NonEmptyDontWarnConfiguration(ProguardClassFilter dontWarnPatterns) {
+ assert dontWarnPatterns != null;
+ assert !dontWarnPatterns.isEmpty();
+ this.dontWarnPatterns = dontWarnPatterns.getPatterns();
+ }
+
+ @Override
+ public Set<DexType> getNonMatches(Set<DexType> types) {
+ Set<DexType> nonMatches = Sets.newIdentityHashSet();
+ for (DexType type : types) {
+ if (!matches(type)) {
+ nonMatches.add(type);
+ }
+ }
+ return nonMatches;
+ }
+
+ @Override
+ public boolean matches(DexType type) {
+ for (ProguardClassNameList dontWarnPattern : dontWarnPatterns) {
+ if (dontWarnPattern.matches(type)) {
+ recordMatch(dontWarnPattern, type);
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private void recordMatch(ProguardClassNameList dontWarnPattern, DexType type) {
+ if (InternalOptions.assertionsEnabled()) {
+ matchedDontWarnPatterns
+ .computeIfAbsent(dontWarnPattern, ignore -> Sets.newIdentityHashSet())
+ .add(type);
+ }
+ }
+
+ @Override
+ public boolean validate(InternalOptions options) {
+ assert options.testing.allowUnnecessaryDontWarnWildcards
+ || validateNoUnnecessaryDontWarnWildcards();
+ assert options.testing.allowUnusedDontWarnRules || validateNoUnusedDontWarnPatterns();
+ return true;
+ }
+
+ public boolean validateNoUnnecessaryDontWarnWildcards() {
+ for (ProguardClassNameList dontWarnPattern : dontWarnPatterns) {
+ assert !dontWarnPattern.hasWildcards()
+ || matchedDontWarnPatterns.getOrDefault(dontWarnPattern, emptySet()).size() != 1
+ : "Unexpected unnecessary wildcard in -dontwarn "
+ + dontWarnPattern.toString()
+ + " (only matches "
+ + matchedDontWarnPatterns.get(dontWarnPattern).iterator().next().getTypeName()
+ + ")";
+ }
+ return true;
+ }
+
+ public boolean validateNoUnusedDontWarnPatterns() {
+ for (ProguardClassNameList dontWarnPattern : dontWarnPatterns) {
+ assert matchedDontWarnPatterns.containsKey(dontWarnPattern)
+ : "Unexpected unused rule -dontwarn " + dontWarnPattern.toString();
+ }
+ return true;
+ }
+}
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 cfacdbb..302dff4 100644
--- a/src/main/java/com/android/tools/r8/graph/AppInfo.java
+++ b/src/main/java/com/android/tools/r8/graph/AppInfo.java
@@ -123,11 +123,6 @@
}
}
- public Collection<DexProgramClass> synthesizedClasses() {
- assert checkIfObsolete();
- return syntheticItems.getPendingSyntheticClasses();
- }
-
public Collection<DexProgramClass> classes() {
assert checkIfObsolete();
return app.classes();
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 56b1a53..8ff4d1e 100644
--- a/src/main/java/com/android/tools/r8/graph/AppInfoWithClassHierarchy.java
+++ b/src/main/java/com/android/tools/r8/graph/AppInfoWithClassHierarchy.java
@@ -15,12 +15,15 @@
import com.android.tools.r8.graph.ResolutionResult.IncompatibleClassResult;
import com.android.tools.r8.graph.ResolutionResult.NoSuchMethodResult;
import com.android.tools.r8.graph.ResolutionResult.SingleResolutionResult;
+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.MissingClasses;
import com.android.tools.r8.synthesis.CommittedItems;
import com.android.tools.r8.synthesis.SyntheticItems;
import com.android.tools.r8.utils.ListUtils;
+import com.android.tools.r8.utils.Pair;
import com.android.tools.r8.utils.SetUtils;
import com.android.tools.r8.utils.TraversalContinuation;
import com.android.tools.r8.utils.TriConsumer;
@@ -287,31 +290,78 @@
}
/** Collect all interfaces that this type directly or indirectly implements. */
- public Set<DexType> implementedInterfaces(DexType type) {
+ public InterfaceCollection implementedInterfaces(DexType type) {
assert type.isClassType();
DexClass clazz = definitionFor(type);
if (clazz == null) {
- return Collections.emptySet();
+ return InterfaceCollection.empty();
}
// Fast path for a type below object with no interfaces.
if (clazz.superType == dexItemFactory().objectType && clazz.interfaces.isEmpty()) {
- return clazz.isInterface() ? Collections.singleton(type) : Collections.emptySet();
+ return clazz.isInterface()
+ ? InterfaceCollection.singleton(type)
+ : InterfaceCollection.empty();
}
// Slow path traverses the full super type hierarchy.
- Set<DexType> interfaces = Sets.newIdentityHashSet();
+ Builder builder = InterfaceCollection.builder();
if (clazz.isInterface()) {
- interfaces.add(type);
+ builder.addInterface(type, true);
}
- forEachSuperType(
- clazz,
- (superType, subclass, isInterface) -> {
- if (isInterface) {
- interfaces.add(superType);
+ // First find all interface leafs from the class super-type chain.
+ Set<DexType> seenAndKnown = Sets.newIdentityHashSet();
+ Deque<Pair<DexClass, Boolean>> worklist = new ArrayDeque<>();
+ {
+ DexClass implementor = clazz;
+ while (implementor != null) {
+ for (DexType iface : implementor.interfaces) {
+ if (seenAndKnown.contains(iface)) {
+ continue;
}
- });
- return interfaces;
+ boolean isKnown =
+ InterfaceCollection.isKnownToImplement(iface, implementor.getType(), options());
+ builder.addInterface(iface, isKnown);
+ if (isKnown) {
+ seenAndKnown.add(iface);
+ }
+ DexClass definition = definitionFor(iface);
+ if (definition != null && !definition.interfaces.isEmpty()) {
+ worklist.add(new Pair<>(definition, isKnown));
+ }
+ }
+ if (implementor.superType == null
+ || implementor.superType == options().dexItemFactory().objectType) {
+ break;
+ }
+ implementor = definitionFor(implementor.superType);
+ }
+ }
+ // Second complete the worklist of interfaces. All paths must be visited as an interface may
+ // be unknown on one but not on another.
+ while (!worklist.isEmpty()) {
+ Pair<DexClass, Boolean> item = worklist.poll();
+ DexClass implementor = item.getFirst();
+ assert !implementor.interfaces.isEmpty();
+ for (DexType itf : implementor.interfaces) {
+ if (seenAndKnown.contains(itf)) {
+ continue;
+ }
+ // A derived interface is known only if the full chain leading to it is known.
+ boolean isKnown =
+ item.getSecond()
+ && InterfaceCollection.isKnownToImplement(itf, implementor.getType(), options());
+ builder.addInterface(itf, isKnown);
+ if (isKnown) {
+ seenAndKnown.add(itf);
+ }
+ DexClass definition = definitionFor(itf);
+ if (definition != null && !definition.interfaces.isEmpty()) {
+ worklist.add(new Pair<>(definition, isKnown));
+ }
+ }
+ }
+ return builder.build();
}
public boolean isExternalizable(DexType type) {
diff --git a/src/main/java/com/android/tools/r8/graph/AppServices.java b/src/main/java/com/android/tools/r8/graph/AppServices.java
index c0c3976..bb61928 100644
--- a/src/main/java/com/android/tools/r8/graph/AppServices.java
+++ b/src/main/java/com/android/tools/r8/graph/AppServices.java
@@ -342,8 +342,7 @@
}
private void warn(String message, DexType type, Origin origin) {
- if (options.getProguardConfiguration() == null
- || !options.getProguardConfiguration().getDontWarnPatterns().matches(type)) {
+ if (!appView.getDontWarnConfiguration().matches(type)) {
options.reporter.warning(new StringDiagnostic(message, origin));
}
}
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 531c78a..489d0b6 100644
--- a/src/main/java/com/android/tools/r8/graph/AppView.java
+++ b/src/main/java/com/android/tools/r8/graph/AppView.java
@@ -4,6 +4,7 @@
package com.android.tools.r8.graph;
+import com.android.tools.r8.errors.dontwarn.DontWarnConfiguration;
import com.android.tools.r8.features.ClassToFeatureSplitMap;
import com.android.tools.r8.graph.DexValue.DexValueString;
import com.android.tools.r8.graph.GraphLens.NonIdentityGraphLens;
@@ -57,6 +58,7 @@
private T appInfo;
private AppInfoWithClassHierarchy appInfoForDesugaring;
private AppServices appServices;
+ private final DontWarnConfiguration dontWarnConfiguration;
private final WholeProgramOptimizations wholeProgramOptimizations;
private GraphLens graphLens;
private InitClassLens initClassLens;
@@ -107,6 +109,7 @@
PrefixRewritingMapper mapper) {
assert appInfo != null;
this.appInfo = appInfo;
+ this.dontWarnConfiguration = DontWarnConfiguration.create(options().getProguardConfiguration());
this.wholeProgramOptimizations = wholeProgramOptimizations;
this.graphLens = GraphLens.getIdentityLens();
this.initClassLens = InitClassLens.getDefault();
@@ -251,6 +254,10 @@
this.appServices = appServices;
}
+ public DontWarnConfiguration getDontWarnConfiguration() {
+ return dontWarnConfiguration;
+ }
+
public boolean isClassEscapingIntoLibrary(DexType type) {
assert type.isClassType();
return classesEscapingIntoLibrary.test(type);
diff --git a/src/main/java/com/android/tools/r8/graph/CfCode.java b/src/main/java/com/android/tools/r8/graph/CfCode.java
index eb83898..4d45d55 100644
--- a/src/main/java/com/android/tools/r8/graph/CfCode.java
+++ b/src/main/java/com/android/tools/r8/graph/CfCode.java
@@ -247,6 +247,9 @@
public void acceptHashing(HashingVisitor visitor) {
// Rather than hash the entire content, hash the sizes and each instruction "type" which
// should provide a fast yet reasonably distinct key.
+ // TODO(b/158159959): This will likely lead to a lot of distinct synthetics hashing to the same
+ // hash as many have the same instruction pattern such as an invoke of the impl method or a
+ // field access.
visitor.visitInt(instructions.size());
visitor.visitInt(tryCatchRanges.size());
visitor.visitInt(localVariables.size());
diff --git a/src/main/java/com/android/tools/r8/graph/ClasspathField.java b/src/main/java/com/android/tools/r8/graph/ClasspathField.java
new file mode 100644
index 0000000..b40e51c
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/graph/ClasspathField.java
@@ -0,0 +1,27 @@
+// 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 class ClasspathField extends DexClassAndField {
+
+ public ClasspathField(DexClasspathClass holder, DexEncodedField field) {
+ super(holder, field);
+ }
+
+ @Override
+ public boolean isClasspathField() {
+ return true;
+ }
+
+ @Override
+ public ClasspathField asClasspathField() {
+ return this;
+ }
+
+ @Override
+ public boolean isClasspathMember() {
+ return true;
+ }
+}
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 9d2a941..0c58053 100644
--- a/src/main/java/com/android/tools/r8/graph/ClasspathMethod.java
+++ b/src/main/java/com/android/tools/r8/graph/ClasspathMethod.java
@@ -23,6 +23,11 @@
}
@Override
+ public boolean isClasspathMember() {
+ return true;
+ }
+
+ @Override
public boolean isClasspathMethod() {
return true;
}
diff --git a/src/main/java/com/android/tools/r8/graph/DexAnnotation.java b/src/main/java/com/android/tools/r8/graph/DexAnnotation.java
index bcb7974..3483fbb 100644
--- a/src/main/java/com/android/tools/r8/graph/DexAnnotation.java
+++ b/src/main/java/com/android/tools/r8/graph/DexAnnotation.java
@@ -14,6 +14,8 @@
import com.android.tools.r8.graph.DexValue.DexValueString;
import com.android.tools.r8.graph.DexValue.DexValueType;
import com.android.tools.r8.ir.desugar.CovariantReturnTypeAnnotationTransformer;
+import com.android.tools.r8.synthesis.SyntheticNaming;
+import com.android.tools.r8.synthesis.SyntheticNaming.SyntheticKind;
import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.Pair;
import com.android.tools.r8.utils.structural.StructuralItem;
@@ -399,13 +401,18 @@
}
public static DexAnnotation createAnnotationSynthesizedClass(
- DexType synthesizingContext, DexItemFactory dexItemFactory) {
- DexValueType value = new DexValueType(synthesizingContext);
- DexAnnotationElement element = new DexAnnotationElement(dexItemFactory.valueString, value);
+ SyntheticKind kind, DexType synthesizingContext, DexItemFactory dexItemFactory) {
+ DexAnnotationElement kindElement =
+ new DexAnnotationElement(
+ dexItemFactory.kindString,
+ new DexValueString(dexItemFactory.createString(kind.descriptor)));
+ DexAnnotationElement typeElement =
+ new DexAnnotationElement(dexItemFactory.valueString, new DexValueType(synthesizingContext));
return new DexAnnotation(
VISIBILITY_BUILD,
new DexEncodedAnnotation(
- dexItemFactory.annotationSynthesizedClass, new DexAnnotationElement[] {element}));
+ dexItemFactory.annotationSynthesizedClass,
+ new DexAnnotationElement[] {kindElement, typeElement}));
}
public static boolean hasSynthesizedClassAnnotation(
@@ -413,7 +420,7 @@
return getSynthesizedClassAnnotationContextType(annotations, factory) != null;
}
- public static DexType getSynthesizedClassAnnotationContextType(
+ public static Pair<SyntheticKind, DexType> getSynthesizedClassAnnotationContextType(
DexAnnotationSet annotations, DexItemFactory factory) {
if (annotations.size() != 1) {
return null;
@@ -422,17 +429,31 @@
if (annotation.annotation.type != factory.annotationSynthesizedClass) {
return null;
}
- if (annotation.annotation.elements.length != 1) {
+ if (annotation.annotation.elements.length != 2) {
return null;
}
- DexAnnotationElement element = annotation.annotation.elements[0];
- if (element.name != factory.valueString) {
+ assert factory.kindString.isLessThan(factory.valueString);
+ DexAnnotationElement kindElement = annotation.annotation.elements[0];
+ if (kindElement.name != factory.kindString) {
return null;
}
- if (!element.value.isDexValueType()) {
+ if (!kindElement.value.isDexValueString()) {
return null;
}
- return element.value.asDexValueType().getValue();
+ SyntheticKind kind =
+ SyntheticNaming.SyntheticKind.fromDescriptor(
+ kindElement.value.asDexValueString().getValue().toString());
+ if (kind == null) {
+ return null;
+ }
+ DexAnnotationElement valueElement = annotation.annotation.elements[1];
+ if (valueElement.name != factory.valueString) {
+ return null;
+ }
+ if (!valueElement.value.isDexValueType()) {
+ return null;
+ }
+ return new Pair<>(kind, valueElement.value.asDexValueType().getValue());
}
public static DexAnnotation createAnnotationSynthesizedClassMap(
diff --git a/src/main/java/com/android/tools/r8/graph/DexApplication.java b/src/main/java/com/android/tools/r8/graph/DexApplication.java
index 6e38ed4..37b8d28 100644
--- a/src/main/java/com/android/tools/r8/graph/DexApplication.java
+++ b/src/main/java/com/android/tools/r8/graph/DexApplication.java
@@ -18,6 +18,7 @@
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
+import java.util.Comparator;
import java.util.List;
public abstract class DexApplication {
@@ -97,6 +98,8 @@
abstract List<DexProgramClass> programClasses();
+ abstract List<DexClasspathClass> classpathClasses();
+
public List<DexProgramClass> classes() {
ReorderBox<DexProgramClass> box = new ReorderBox<>(programClasses());
assert box.reorderClasses();
@@ -104,13 +107,16 @@
}
public List<DexProgramClass> classesWithDeterministicOrder() {
- List<DexProgramClass> classes = new ArrayList<>(programClasses());
- // We never actually sort by anything but the DexType, this is just here in case we ever change
- // that.
- if (options.testing.deterministicSortingBasedOnDexType) {
- // To keep the order deterministic, we sort the classes by their type, which is a unique key.
- classes.sort((a, b) -> a.type.compareTo(b.type));
- }
+ return classesWithDeterministicOrder(new ArrayList<>(programClasses()));
+ }
+
+ public static <T extends DexClass> List<T> classesWithDeterministicOrder(Collection<T> classes) {
+ return classesWithDeterministicOrder(new ArrayList<>(classes));
+ }
+
+ public static <T extends DexClass> List<T> classesWithDeterministicOrder(List<T> classes) {
+ // To keep the order deterministic, we sort the classes by their type, which is a unique key.
+ classes.sort(Comparator.comparing(DexClass::getType));
return classes;
}
@@ -126,14 +132,9 @@
}
public abstract static class Builder<T extends Builder<T>> {
- // We handle program class collection separately from classpath
- // and library class collections. Since while we assume program
- // class collection should always be fully loaded and thus fully
- // represented by the map (making it easy, for example, adding
- // new or removing existing classes), classpath and library
- // collections will be considered monolithic collections.
- final List<DexProgramClass> programClasses = new ArrayList<>();
+ private final List<DexProgramClass> programClasses = new ArrayList<>();
+ private final List<DexClasspathClass> classpathClasses = new ArrayList<>();
final List<DataResourceProvider> dataResourceProviders = new ArrayList<>();
@@ -156,6 +157,7 @@
public Builder(DexApplication application) {
programClasses.addAll(application.programClasses());
+ classpathClasses.addAll(application.classpathClasses());
dataResourceProviders.addAll(application.dataResourceProviders);
proguardMap = application.getProguardMap();
timing = application.timing;
@@ -171,13 +173,21 @@
return self();
}
- public synchronized T replaceProgramClasses(List<DexProgramClass> newProgramClasses) {
+ public synchronized T replaceProgramClasses(Collection<DexProgramClass> newProgramClasses) {
assert newProgramClasses != null;
this.programClasses.clear();
this.programClasses.addAll(newProgramClasses);
return self();
}
+ public synchronized T replaceClasspathClasses(
+ Collection<DexClasspathClass> newClasspathClasses) {
+ assert newClasspathClasses != null;
+ classpathClasses.clear();
+ classpathClasses.addAll(newClasspathClasses);
+ return self();
+ }
+
public synchronized T addDataResourceProvider(DataResourceProvider provider) {
dataResourceProviders.add(provider);
return self();
@@ -193,6 +203,21 @@
return self();
}
+ public synchronized T addProgramClasses(Collection<DexProgramClass> classes) {
+ programClasses.addAll(classes);
+ return self();
+ }
+
+ public synchronized T addClasspathClass(DexClasspathClass clazz) {
+ classpathClasses.add(clazz);
+ return self();
+ }
+
+ public synchronized T addClasspathClasses(Collection<DexClasspathClass> classes) {
+ classpathClasses.addAll(classes);
+ return self();
+ }
+
public synchronized T addSynthesizedClass(DexProgramClass synthesizedClass) {
assert synthesizedClass.isProgramClass() : "All synthesized classes must be program classes";
addProgramClass(synthesizedClass);
@@ -200,10 +225,14 @@
return self();
}
- public Collection<DexProgramClass> getProgramClasses() {
+ public List<DexProgramClass> getProgramClasses() {
return programClasses;
}
+ public List<DexClasspathClass> getClasspathClasses() {
+ return classpathClasses;
+ }
+
public Collection<DexProgramClass> getSynthesizedClasses() {
return synthesizedClasses;
}
diff --git a/src/main/java/com/android/tools/r8/graph/DexByteCodeWriter.java b/src/main/java/com/android/tools/r8/graph/DexByteCodeWriter.java
index d24a75b..60ab622 100644
--- a/src/main/java/com/android/tools/r8/graph/DexByteCodeWriter.java
+++ b/src/main/java/com/android/tools/r8/graph/DexByteCodeWriter.java
@@ -3,6 +3,7 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.graph;
+import com.android.tools.r8.dex.Marker;
import com.android.tools.r8.utils.DescriptorUtils;
import com.android.tools.r8.utils.InternalOptions;
import java.io.File;
@@ -10,6 +11,7 @@
import java.io.PrintStream;
import java.nio.file.Files;
import java.nio.file.Path;
+import java.util.List;
import java.util.function.Consumer;
public abstract class DexByteCodeWriter {
@@ -57,8 +59,12 @@
}
public void write(PrintStream output) throws IOException {
- write(x -> output, x -> {
- });
+ List<Marker> markers = application.dexItemFactory.extractMarkers();
+ System.out.println("Number of markers: " + markers.size());
+ for (Marker marker : markers) {
+ output.println(marker.toString());
+ }
+ write(x -> output, x -> {});
}
private void write(OutputStreamProvider outputStreamProvider, Consumer<PrintStream> closer)
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 22a830e..f1c0a2f 100644
--- a/src/main/java/com/android/tools/r8/graph/DexClass.java
+++ b/src/main/java/com/android/tools/r8/graph/DexClass.java
@@ -122,6 +122,11 @@
}
}
+ public abstract void accept(
+ Consumer<DexProgramClass> programClassConsumer,
+ Consumer<DexClasspathClass> classpathClassConsumer,
+ Consumer<DexLibraryClass> libraryClassConsumer);
+
public void forEachClassMethod(Consumer<? super DexClassAndMethod> consumer) {
forEachClassMethodMatching(alwaysTrue(), consumer);
}
@@ -163,6 +168,10 @@
return Iterables.concat(fields(), methods());
}
+ public Iterable<DexEncodedMember<?, ?>> members(Predicate<DexEncodedMember<?, ?>> predicate) {
+ return Iterables.concat(fields(predicate), methods(predicate));
+ }
+
public MethodCollection getMethodCollection() {
return methodCollection;
}
@@ -171,7 +180,7 @@
return methodCollection.methods();
}
- public Iterable<DexEncodedMethod> methods(Predicate<DexEncodedMethod> predicate) {
+ public Iterable<DexEncodedMethod> methods(Predicate<? super DexEncodedMethod> predicate) {
return methodCollection.methods(predicate);
}
@@ -445,6 +454,15 @@
return field;
}
+ /** Find method in this class matching {@param method}. */
+ public DexClassAndField lookupClassField(DexField field) {
+ return toClassFieldOrNull(lookupField(field));
+ }
+
+ private DexClassAndField toClassFieldOrNull(DexEncodedField field) {
+ return field != null ? DexClassAndField.create(this, field) : null;
+ }
+
/** Find field in this class matching {@param field}. */
public DexEncodedField lookupField(DexField field) {
DexEncodedField result = lookupInstanceField(field);
@@ -848,6 +866,10 @@
nestHost = null;
}
+ public void clearNestMembers() {
+ nestMembers.clear();
+ }
+
public void setNestHost(DexType type) {
assert type != null;
this.nestHost = new NestHostClassAttribute(type);
@@ -887,6 +909,10 @@
/** Returns kotlin class info if the class is synthesized by kotlin compiler. */
public abstract KotlinClassLevelInfo getKotlinInfo();
+ public final String getTypeName() {
+ return getType().getTypeName();
+ }
+
public boolean hasStaticFields() {
return staticFields.length > 0;
}
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 bba0f82..cef86d0 100644
--- a/src/main/java/com/android/tools/r8/graph/DexClassAndField.java
+++ b/src/main/java/com/android/tools/r8/graph/DexClassAndField.java
@@ -8,15 +8,20 @@
DexClassAndField(DexClass holder, DexEncodedField field) {
super(holder, field);
+ assert holder.isClasspathClass() == (this instanceof ClasspathField);
+ assert holder.isLibraryClass() == (this instanceof LibraryField);
assert holder.isProgramClass() == (this instanceof ProgramField);
}
public static DexClassAndField create(DexClass holder, DexEncodedField field) {
if (holder.isProgramClass()) {
return new ProgramField(holder.asProgramClass(), field);
- } else {
- return new DexClassAndField(holder, field);
}
+ if (holder.isLibraryClass()) {
+ return new LibraryField(holder.asLibraryClass(), field);
+ }
+ assert holder.isClasspathClass();
+ return new ClasspathField(holder.asClasspathClass(), field);
}
@Override
@@ -28,6 +33,22 @@
return getReference().getType();
}
+ public boolean isClasspathField() {
+ return false;
+ }
+
+ public ClasspathField asClasspathField() {
+ return null;
+ }
+
+ public boolean isLibraryField() {
+ return false;
+ }
+
+ public LibraryField asLibraryField() {
+ return null;
+ }
+
public boolean isProgramField() {
return false;
}
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 719e2d6..83c4334 100644
--- a/src/main/java/com/android/tools/r8/graph/DexClassAndMember.java
+++ b/src/main/java/com/android/tools/r8/graph/DexClassAndMember.java
@@ -51,6 +51,18 @@
return holder.origin;
}
+ public boolean isClasspathMember() {
+ return false;
+ }
+
+ public boolean isLibraryMember() {
+ return false;
+ }
+
+ public boolean isProgramMember() {
+ return false;
+ }
+
public String toSourceString() {
return getReference().toSourceString();
}
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 0446e09..075c1ba 100644
--- a/src/main/java/com/android/tools/r8/graph/DexClassAndMethod.java
+++ b/src/main/java/com/android/tools/r8/graph/DexClassAndMethod.java
@@ -9,6 +9,8 @@
DexClassAndMethod(DexClass holder, DexEncodedMethod method) {
super(holder, method);
+ assert holder.isClasspathClass() == (this instanceof ClasspathMethod);
+ assert holder.isLibraryClass() == (this instanceof LibraryMethod);
assert holder.isProgramClass() == (this instanceof ProgramMethod);
}
@@ -19,12 +21,12 @@
public static DexClassAndMethod create(DexClass holder, DexEncodedMethod method) {
if (holder.isProgramClass()) {
return new ProgramMethod(holder.asProgramClass(), method);
- } else if (holder.isLibraryClass()) {
- return new LibraryMethod(holder.asLibraryClass(), method);
- } else {
- assert holder.isClasspathClass();
- return new ClasspathMethod(holder.asClasspathClass(), method);
}
+ if (holder.isLibraryClass()) {
+ return new LibraryMethod(holder.asLibraryClass(), method);
+ }
+ assert holder.isClasspathClass();
+ return new ClasspathMethod(holder.asClasspathClass(), method);
}
public boolean isDefaultMethod() {
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 c97a1a4..7fb6023 100644
--- a/src/main/java/com/android/tools/r8/graph/DexClasspathClass.java
+++ b/src/main/java/com/android/tools/r8/graph/DexClasspathClass.java
@@ -3,6 +3,8 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.graph;
+import static com.google.common.base.Predicates.alwaysTrue;
+
import com.android.tools.r8.ProgramResource;
import com.android.tools.r8.ProgramResource.Kind;
import com.android.tools.r8.dex.MixedSectionCollection;
@@ -10,12 +12,17 @@
import com.android.tools.r8.graph.GenericSignature.ClassSignature;
import com.android.tools.r8.kotlin.KotlinClassLevelInfo;
import com.android.tools.r8.origin.Origin;
+import com.android.tools.r8.utils.structural.StructuralItem;
+import com.android.tools.r8.utils.structural.StructuralMapping;
+import com.android.tools.r8.utils.structural.StructuralSpecification;
import java.util.List;
import java.util.Set;
+import java.util.function.Consumer;
import java.util.function.Predicate;
import java.util.function.Supplier;
-public class DexClasspathClass extends DexClass implements Supplier<DexClasspathClass> {
+public class DexClasspathClass extends DexClass
+ implements Supplier<DexClasspathClass>, StructuralItem<DexClasspathClass> {
public DexClasspathClass(
DexType type,
@@ -58,6 +65,24 @@
}
@Override
+ public void accept(
+ Consumer<DexProgramClass> programClassConsumer,
+ Consumer<DexClasspathClass> classpathClassConsumer,
+ Consumer<DexLibraryClass> libraryClassConsumer) {
+ classpathClassConsumer.accept(this);
+ }
+
+ public void forEachClasspathMethod(Consumer<? super ClasspathMethod> consumer) {
+ forEachClasspathMethodMatching(alwaysTrue(), consumer);
+ }
+
+ public void forEachClasspathMethodMatching(
+ Predicate<DexEncodedMethod> predicate, Consumer<? super ClasspathMethod> consumer) {
+ methodCollection.forEachMethodMatching(
+ predicate, method -> consumer.accept(new ClasspathMethod(this, method)));
+ }
+
+ @Override
public String toString() {
return type.toString() + "(classpath class)";
}
@@ -78,6 +103,10 @@
return this;
}
+ public static DexClasspathClass asClasspathClassOrNull(DexClass clazz) {
+ return clazz != null ? clazz.asClasspathClass() : null;
+ }
+
@Override
public boolean isNotProgramClass() {
return true;
@@ -104,4 +133,30 @@
}
return !isInterface() || appView.options().classpathInterfacesMayHaveStaticInitialization;
}
+
+ @Override
+ public DexClasspathClass self() {
+ return this;
+ }
+
+ @Override
+ public StructuralMapping<DexClasspathClass> getStructuralMapping() {
+ return DexClasspathClass::specify;
+ }
+
+ private static void specify(StructuralSpecification<DexClasspathClass, ?> spec) {
+ spec.withItem(DexClass::getType)
+ .withItem(DexClass::getSuperType)
+ .withItem(DexClass::getInterfaces)
+ .withItem(DexClass::getAccessFlags)
+ .withNullableItem(DexClass::getSourceFile)
+ .withNullableItem(DexClass::getNestHostClassAttribute)
+ .withItemCollection(DexClass::getNestMembersClassAttributes)
+ .withItem(DexDefinition::annotations)
+ // TODO(b/158159959): Make signatures structural.
+ .withAssert(c -> c.classSignature == ClassSignature.noSignature())
+ .withItemArray(c -> c.staticFields)
+ .withItemArray(c -> c.instanceFields)
+ .withItemCollection(DexClass::allMethodsSorted);
+ }
}
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 fc0eeb0..30d5cfd 100644
--- a/src/main/java/com/android/tools/r8/graph/DexEncodedField.java
+++ b/src/main/java/com/android/tools/r8/graph/DexEncodedField.java
@@ -203,10 +203,6 @@
return accessFlags.isPackagePrivate();
}
- public boolean isPrivate() {
- return accessFlags.isPrivate();
- }
-
public boolean isProtected() {
return accessFlags.isProtected();
}
diff --git a/src/main/java/com/android/tools/r8/graph/DexEncodedMember.java b/src/main/java/com/android/tools/r8/graph/DexEncodedMember.java
index bca8a6b..d5c5135 100644
--- a/src/main/java/com/android/tools/r8/graph/DexEncodedMember.java
+++ b/src/main/java/com/android/tools/r8/graph/DexEncodedMember.java
@@ -35,6 +35,10 @@
return this;
}
+ public final boolean isPrivate() {
+ return getAccessFlags().isPrivate();
+ }
+
public abstract ProgramMember<D, R> asProgramMember(DexDefinitionSupplier definitions);
@Override
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 91203c8..fc56e08 100644
--- a/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
+++ b/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
@@ -49,7 +49,6 @@
import com.android.tools.r8.ir.code.NumericType;
import com.android.tools.r8.ir.code.ValueType;
import com.android.tools.r8.ir.conversion.DexBuilder;
-import com.android.tools.r8.ir.desugar.NestBasedAccessDesugaring.DexFieldWithAccess;
import com.android.tools.r8.ir.optimize.Inliner.ConstraintWithTarget;
import com.android.tools.r8.ir.optimize.Inliner.Reason;
import com.android.tools.r8.ir.optimize.NestUtils;
@@ -60,7 +59,8 @@
import com.android.tools.r8.ir.optimize.inliner.WhyAreYouNotInliningReporter;
import com.android.tools.r8.ir.regalloc.RegisterAllocator;
import com.android.tools.r8.ir.synthetic.EmulateInterfaceSyntheticCfCodeProvider;
-import com.android.tools.r8.ir.synthetic.FieldAccessorSourceCode;
+import com.android.tools.r8.ir.synthetic.FieldAccessorBuilder;
+import com.android.tools.r8.ir.synthetic.ForwardMethodBuilder;
import com.android.tools.r8.ir.synthetic.ForwardMethodSourceCode;
import com.android.tools.r8.ir.synthetic.SynthesizedCode;
import com.android.tools.r8.kotlin.KotlinMethodLevelInfo;
@@ -76,14 +76,12 @@
import com.android.tools.r8.utils.OptionalBool;
import com.android.tools.r8.utils.Pair;
import com.android.tools.r8.utils.structural.CompareToVisitor;
-import com.android.tools.r8.utils.structural.CompareToVisitorWithTypeEquivalence;
import com.android.tools.r8.utils.structural.HashingVisitor;
-import com.android.tools.r8.utils.structural.HashingVisitorWithTypeEquivalence;
import com.android.tools.r8.utils.structural.Ordered;
-import com.android.tools.r8.utils.structural.RepresentativeMap;
+import com.android.tools.r8.utils.structural.StructuralItem;
+import com.android.tools.r8.utils.structural.StructuralMapping;
import com.android.tools.r8.utils.structural.StructuralSpecification;
import com.google.common.collect.ImmutableList;
-import com.google.common.hash.Hasher;
import it.unimi.dsi.fastutil.ints.Int2ReferenceArrayMap;
import it.unimi.dsi.fastutil.ints.Int2ReferenceMap;
import java.util.ArrayList;
@@ -95,7 +93,8 @@
import java.util.function.IntPredicate;
import org.objectweb.asm.Opcodes;
-public class DexEncodedMethod extends DexEncodedMember<DexEncodedMethod, DexMethod> {
+public class DexEncodedMethod extends DexEncodedMember<DexEncodedMethod, DexMethod>
+ implements StructuralItem<DexEncodedMethod> {
public static final String CONFIGURATION_DEBUGGING_PREFIX = "Shaking error: Missing method in ";
@@ -338,12 +337,22 @@
return deprecated;
}
+ @Override
+ public DexEncodedMethod self() {
+ return this;
+ }
+
+ @Override
+ public StructuralMapping<DexEncodedMethod> getStructuralMapping() {
+ return DexEncodedMethod::syntheticSpecify;
+ }
+
// Visitor specifying the structure of the method with respect to its "synthetic" content.
// TODO(b/171867022): Generalize this so that it determines any method in full.
private static void syntheticSpecify(StructuralSpecification<DexEncodedMethod, ?> spec) {
spec.withItem(m -> m.method)
.withItem(m -> m.accessFlags)
- .withItem(m -> m.annotations())
+ .withItem(DexDefinition::annotations)
.withItem(m -> m.parameterAnnotationsList)
.withNullableItem(m -> m.classFileVersion)
.withBool(m -> m.d8R8Synthesized)
@@ -356,28 +365,12 @@
DexEncodedMethod::hashCodeObject);
}
- public void hashSyntheticContent(Hasher hasher, RepresentativeMap map) {
- HashingVisitorWithTypeEquivalence.run(this, hasher, map, DexEncodedMethod::syntheticSpecify);
- }
-
- public boolean isSyntheticContentEqual(DexEncodedMethod other) {
- return syntheticCompareTo(other) == 0;
- }
-
- public int syntheticCompareTo(DexEncodedMethod other) {
- // Consider the holder types to be equivalent, using the holder of this method as the
- // representative.
- RepresentativeMap map = t -> t == other.getHolderType() ? getHolderType() : t;
- return CompareToVisitorWithTypeEquivalence.run(
- this, other, map, DexEncodedMethod::syntheticSpecify);
- }
-
private static int compareCodeObject(Code code1, Code code2, CompareToVisitor visitor) {
if (code1.isCfCode() && code2.isCfCode()) {
return code1.asCfCode().acceptCompareTo(code2.asCfCode(), visitor);
}
if (code1.isDexCode() && code2.isDexCode()) {
- return visitor.visit(code1.asDexCode(), code2.asDexCode(), DexCode::compareTo);
+ return code1.asDexCode().acceptCompareTo(code2.asDexCode(), visitor);
}
throw new Unreachable(
"Unexpected attempt to compare incompatible synthetic objects: " + code1 + " and " + code2);
@@ -387,14 +380,12 @@
if (code.isCfCode()) {
code.asCfCode().acceptHashing(visitor);
} else {
- // TODO(b/158159959): Implement a more precise hashing on code objects.
- assert code.isDexCode();
- visitor.visitInt(code.hashCode());
+ code.asDexCode().acceptHashing(visitor);
}
}
public DexProto getProto() {
- return getReference().proto;
+ return getReference().getProto();
}
@Override
@@ -411,10 +402,6 @@
return method.proto.parameters;
}
- public DexProto proto() {
- return method.proto;
- }
-
public DexType returnType() {
return method.proto.returnType;
}
@@ -506,10 +493,6 @@
return accessFlags.isNative();
}
- public boolean isPrivate() {
- return accessFlags.isPrivate();
- }
-
public boolean isPublic() {
return accessFlags.isPublic();
}
@@ -1016,6 +999,10 @@
}
public CfCode buildEmptyThrowingCfCode() {
+ return buildEmptyThrowingCfCode(method);
+ }
+
+ public static CfCode buildEmptyThrowingCfCode(DexMethod method) {
CfInstruction insn[] = {new CfConstNull(), new CfThrow()};
return new CfCode(
method.holder,
@@ -1201,59 +1188,47 @@
return builder.build();
}
- public ProgramMethod toInitializerForwardingBridge(DexProgramClass holder, DexMethod newMethod) {
- assert accessFlags.isPrivate()
+ public ProgramMethod toInitializerForwardingBridge(
+ DexProgramClass holder, DexMethod newMethod, DexItemFactory dexItemFactory) {
+ assert isPrivate()
: "Expected to create bridge for private constructor as part of nest-based access"
+ " desugaring";
- Builder builder = syntheticBuilder(this);
- builder.setMethod(newMethod);
- ForwardMethodSourceCode.Builder forwardSourceCodeBuilder =
- ForwardMethodSourceCode.builder(newMethod);
- forwardSourceCodeBuilder
- .setReceiver(holder.type)
- .setTargetReceiver(holder.type)
- .setTarget(method)
- .setInvokeType(Invoke.Type.DIRECT)
- .setExtraNullParameter();
- builder.setCode(
- new SynthesizedCode(
- forwardSourceCodeBuilder::build, registry -> registry.registerInvokeDirect(method)));
- assert !builder.accessFlags.isStatic();
assert !holder.isInterface();
- builder.accessFlags.unsetPrivate();
- builder.accessFlags.setSynthetic();
- builder.accessFlags.setConstructor();
- return new ProgramMethod(holder, builder.build());
+ return new ProgramMethod(
+ holder,
+ syntheticBuilder(this)
+ .setMethod(newMethod)
+ .setCode(
+ ForwardMethodBuilder.builder(dexItemFactory)
+ .setNonStaticSourceWithExtraUnusedParameter(newMethod)
+ .setConstructorTarget(getReference())
+ .build())
+ .modifyAccessFlags(
+ accessFlags -> {
+ assert !accessFlags.isStatic();
+ accessFlags.unsetPrivate();
+ accessFlags.setSynthetic();
+ accessFlags.setConstructor();
+ })
+ .build());
}
public static ProgramMethod createFieldAccessorBridge(
- DexFieldWithAccess fieldWithAccess, DexProgramClass holder, DexMethod newMethod) {
- assert holder.type == fieldWithAccess.getHolder();
+ ProgramField field, boolean isGet, DexMethod newMethod) {
MethodAccessFlags accessFlags =
MethodAccessFlags.fromSharedAccessFlags(
Constants.ACC_SYNTHETIC
| Constants.ACC_STATIC
- | (holder.isInterface() ? Constants.ACC_PUBLIC : 0),
+ | (field.getHolder().isInterface() ? Constants.ACC_PUBLIC : 0),
false);
- Code code =
- new SynthesizedCode(
- callerPosition ->
- new FieldAccessorSourceCode(
- null, newMethod, callerPosition, newMethod, fieldWithAccess),
- registry -> {
- if (fieldWithAccess.isInstanceGet()) {
- registry.registerInstanceFieldRead(fieldWithAccess.getField());
- } else if (fieldWithAccess.isStaticGet()) {
- registry.registerStaticFieldRead(fieldWithAccess.getField());
- } else if (fieldWithAccess.isInstancePut()) {
- registry.registerInstanceFieldWrite(fieldWithAccess.getField());
- } else {
- assert fieldWithAccess.isStaticPut();
- registry.registerStaticFieldWrite(fieldWithAccess.getField());
- }
- });
+ CfCode code =
+ FieldAccessorBuilder.builder()
+ .apply(isGet ? FieldAccessorBuilder::setGetter : FieldAccessorBuilder::setSetter)
+ .setField(field)
+ .setSourceMethod(newMethod)
+ .build();
return new ProgramMethod(
- holder,
+ field.getHolder(),
new DexEncodedMethod(
newMethod,
accessFlags,
@@ -1294,35 +1269,36 @@
true);
}
- public ProgramMethod toStaticForwardingBridge(DexProgramClass holder, DexMethod newMethod) {
- assert accessFlags.isPrivate()
+ public ProgramMethod toStaticForwardingBridge(
+ DexProgramClass holder, DexMethod newMethod, DexItemFactory dexItemFactory) {
+ assert isPrivate()
: "Expected to create bridge for private method as part of nest-based access desugaring";
- Builder builder = syntheticBuilder(this);
- builder.setMethod(newMethod);
- ForwardMethodSourceCode.Builder forwardSourceCodeBuilder =
- ForwardMethodSourceCode.builder(newMethod);
- forwardSourceCodeBuilder
- .setTargetReceiver(accessFlags.isStatic() ? null : method.holder)
- .setTarget(method)
- .setInvokeType(accessFlags.isStatic() ? Invoke.Type.STATIC : Invoke.Type.DIRECT)
- .setIsInterface(holder.isInterface());
- builder.setCode(
- new SynthesizedCode(
- forwardSourceCodeBuilder::build,
- registry -> {
- if (accessFlags.isStatic()) {
- registry.registerInvokeStatic(method);
- } else {
- registry.registerInvokeDirect(method);
- }
- }));
- builder.accessFlags.setSynthetic();
- builder.accessFlags.setStatic();
- builder.accessFlags.unsetPrivate();
- if (holder.isInterface()) {
- builder.accessFlags.setPublic();
- }
- return new ProgramMethod(holder, builder.build());
+ return new ProgramMethod(
+ holder,
+ syntheticBuilder(this)
+ .setMethod(newMethod)
+ .setCode(
+ ForwardMethodBuilder.builder(dexItemFactory)
+ .setStaticSource(newMethod)
+ .apply(
+ builder -> {
+ if (isStatic()) {
+ builder.setStaticTarget(getReference(), holder.isInterface());
+ } else {
+ builder.setDirectTarget(getReference(), holder.isInterface());
+ }
+ })
+ .build())
+ .modifyAccessFlags(
+ accessFlags -> {
+ accessFlags.setSynthetic();
+ accessFlags.setStatic();
+ accessFlags.unsetPrivate();
+ if (holder.isInterface()) {
+ accessFlags.setPublic();
+ }
+ })
+ .build());
}
public DexEncodedMethod toPrivateSyntheticMethod(DexMethod method) {
@@ -1596,12 +1572,18 @@
return this;
}
+ public Builder modifyAccessFlags(Consumer<MethodAccessFlags> consumer) {
+ consumer.accept(accessFlags);
+ return this;
+ }
+
public void setAccessFlags(MethodAccessFlags accessFlags) {
this.accessFlags = accessFlags.copy();
}
- public void setMethod(DexMethod method) {
+ public Builder setMethod(DexMethod method) {
this.method = method;
+ return this;
}
public Builder setCompilationState(CompilationState compilationState) {
@@ -1686,8 +1668,9 @@
return this;
}
- public void setCode(Code code) {
+ public Builder setCode(Code code) {
this.code = code;
+ return this;
}
public DexEncodedMethod build() {
diff --git a/src/main/java/com/android/tools/r8/graph/DexField.java b/src/main/java/com/android/tools/r8/graph/DexField.java
index e9c2fc5..a925fcc 100644
--- a/src/main/java/com/android/tools/r8/graph/DexField.java
+++ b/src/main/java/com/android/tools/r8/graph/DexField.java
@@ -52,6 +52,11 @@
}
@Override
+ public DexClassAndField lookupMemberOnClass(DexClass clazz) {
+ return clazz != null ? clazz.lookupClassField(this) : null;
+ }
+
+ @Override
public ProgramField lookupOnProgramClass(DexProgramClass clazz) {
return clazz != null ? clazz.lookupProgramField(this) : null;
}
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 e6d824a..fd33483 100644
--- a/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
+++ b/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
@@ -4,7 +4,6 @@
package com.android.tools.r8.graph;
import static com.android.tools.r8.ir.analysis.type.ClassTypeElement.computeLeastUpperBoundOfInterfaces;
-import static com.android.tools.r8.ir.optimize.ServiceLoaderRewriter.SERVICE_LOADER_CLASS_NAME;
import com.android.tools.r8.dex.Constants;
import com.android.tools.r8.dex.Marker;
@@ -20,12 +19,13 @@
import com.android.tools.r8.graph.DexMethodHandle.MethodHandleType;
import com.android.tools.r8.ir.analysis.type.ArrayTypeElement;
import com.android.tools.r8.ir.analysis.type.ClassTypeElement;
+import com.android.tools.r8.ir.analysis.type.InterfaceCollection;
import com.android.tools.r8.ir.analysis.type.Nullability;
import com.android.tools.r8.ir.analysis.type.ReferenceTypeElement;
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.NestBasedAccessDesugaring;
+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;
@@ -44,7 +44,6 @@
import it.unimi.dsi.fastutil.ints.Int2ReferenceOpenHashMap;
import java.util.ArrayList;
import java.util.Arrays;
-import java.util.Collections;
import java.util.HashMap;
import java.util.IdentityHashMap;
import java.util.Iterator;
@@ -90,9 +89,9 @@
// ReferenceTypeElement canonicalization.
private final ConcurrentHashMap<DexType, ReferenceTypeElement> referenceTypes =
new ConcurrentHashMap<>();
- private final ConcurrentHashMap<DexType, Set<DexType>> classTypeInterfaces =
+ private final ConcurrentHashMap<DexType, InterfaceCollection> classTypeInterfaces =
new ConcurrentHashMap<>();
- public final LRUCacheTable<Set<DexType>, Set<DexType>, Set<DexType>>
+ public final LRUCacheTable<InterfaceCollection, InterfaceCollection, InterfaceCollection>
leastUpperBoundOfInterfacesTable = LRUCacheTable.create(8, 8);
boolean sorted = false;
@@ -231,6 +230,8 @@
public final DexString iterableDescriptor = createString("Ljava/lang/Iterable;");
public final DexString mathDescriptor = createString("Ljava/lang/Math;");
public final DexString strictMathDescriptor = createString("Ljava/lang/StrictMath;");
+ public final DexString closeableDescriptor = createString("Ljava/io/Closeable;");
+ public final DexString zipFileDescriptor = createString("Ljava/util/zip/ZipFile;");
public final DexString stringBuilderDescriptor = createString("Ljava/lang/StringBuilder;");
public final DexString stringBufferDescriptor = createString("Ljava/lang/StringBuffer;");
@@ -292,6 +293,11 @@
createString(Constants.TEMPORARY_INSTANCE_INITIALIZER_PREFIX);
public final DexString thisName = createString("this");
+
+ // As much as possible, R8 should rely on the content of the static enum field, using
+ // enumMembers.isValuesFieldCandidate or checking the object state in the optimization info.
+ // The field name is unrealiable since the filed can be minified prior to this compilation.
+ // We keep enumValuesFieldName as a heuristic only.
public final DexString enumValuesFieldName = createString("$VALUES");
public final DexString enabledFieldName = createString("ENABLED");
@@ -299,6 +305,7 @@
public final DexString throwableArrayDescriptor = createString("[Ljava/lang/Throwable;");
public final DexString valueString = createString("value");
+ public final DexString kindString = createString("kind");
public final DexType booleanType = createStaticallyKnownType(booleanDescriptor);
public final DexType byteType = createStaticallyKnownType(byteDescriptor);
@@ -356,6 +363,9 @@
public final DexType methodType = createStaticallyKnownType(methodDescriptor);
public final DexType autoCloseableType = createStaticallyKnownType(autoCloseableDescriptor);
+ public final DexType closeableType = createStaticallyKnownType(closeableDescriptor);
+ public final DexType zipFileType = createStaticallyKnownType(zipFileDescriptor);
+
public final DexType stringBuilderType = createStaticallyKnownType(stringBuilderDescriptor);
public final DexType stringBufferType = createStaticallyKnownType(stringBufferDescriptor);
@@ -369,8 +379,6 @@
createStaticallyKnownType(invocationHandlerDescriptor);
public final DexType proxyType = createStaticallyKnownType(proxyDescriptor);
public final DexType serviceLoaderType = createStaticallyKnownType(serviceLoaderDescriptor);
- public final DexType serviceLoaderRewrittenClassType =
- createStaticallyKnownType("L" + SERVICE_LOADER_CLASS_NAME + ";");
public final DexType serviceLoaderConfigurationErrorType =
createStaticallyKnownType(serviceLoaderConfigurationErrorDescriptor);
public final DexType listType = createStaticallyKnownType(listDescriptor);
@@ -409,6 +417,8 @@
public final DexType noClassDefFoundErrorType =
createStaticallyKnownType(noClassDefFoundErrorDescriptor);
public final DexType noSuchFieldErrorType = createStaticallyKnownType(noSuchFieldErrorDescriptor);
+ public final DexType noSuchMethodErrorType =
+ createStaticallyKnownType("Ljava/lang/NoSuchMethodError;");
public final DexType npeType = createStaticallyKnownType(npeDescriptor);
public final DexType reflectiveOperationExceptionType =
createStaticallyKnownType(reflectiveOperationExceptionDescriptor);
@@ -1380,21 +1390,17 @@
return field == nameField || field == ordinalField;
}
- public boolean isValuesMethod(DexMethod method, DexClass enumClass) {
- assert enumClass.isEnum();
- return method.holder == enumClass.type
- && method.proto.returnType == enumClass.type.toArrayType(1, DexItemFactory.this)
- && method.proto.parameters.size() == 0
- && method.name == valuesMethodName;
+ public boolean isEnumField(DexEncodedField staticField, DexType enumType) {
+ assert staticField.isStatic();
+ return staticField.getType() == enumType && staticField.isEnum() && staticField.isFinal();
}
- public boolean isValueOfMethod(DexMethod method, DexClass enumClass) {
- assert enumClass.isEnum();
- return method.holder == enumClass.type
- && method.proto.returnType == enumClass.type
- && method.proto.parameters.size() == 1
- && method.proto.parameters.values[0] == stringType
- && method.name == valueOfMethodName;
+ public boolean isValuesFieldCandidate(DexEncodedField staticField, DexType enumType) {
+ assert staticField.isStatic();
+ return staticField.getType().isArrayType()
+ && staticField.getType().toArrayElementType(DexItemFactory.this) == enumType
+ && staticField.isSynthetic()
+ && staticField.isFinal();
}
}
@@ -2420,12 +2426,12 @@
if (type.isClassType()) {
if (!appView.enableWholeProgramOptimizations()) {
// Don't reason at the level of interfaces in D8.
- return ClassTypeElement.create(type, nullability, Collections.emptySet());
+ return ClassTypeElement.create(type, nullability, InterfaceCollection.empty());
}
assert appView.appInfo().hasClassHierarchy();
if (appView.isInterface(type).isTrue()) {
return ClassTypeElement.create(
- objectType, nullability, Collections.singleton(type));
+ objectType, nullability, InterfaceCollection.singleton(type));
}
// In theory, `interfaces` is the least upper bound of implemented interfaces.
// It is expensive to walk through type hierarchy; collect implemented interfaces;
@@ -2440,12 +2446,12 @@
.getOrCreateVariant(nullability);
}
- public Set<DexType> getOrComputeLeastUpperBoundOfImplementedInterfaces(
+ public InterfaceCollection getOrComputeLeastUpperBoundOfImplementedInterfaces(
DexType type, AppView<? extends AppInfoWithClassHierarchy> appView) {
return classTypeInterfaces.computeIfAbsent(
type,
t -> {
- Set<DexType> itfs = appView.appInfo().implementedInterfaces(t);
+ InterfaceCollection itfs = appView.appInfo().implementedInterfaces(t);
return computeLeastUpperBoundOfInterfaces(appView, itfs, itfs);
});
}
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 e5a8097..df36fb7 100644
--- a/src/main/java/com/android/tools/r8/graph/DexLibraryClass.java
+++ b/src/main/java/com/android/tools/r8/graph/DexLibraryClass.java
@@ -13,6 +13,7 @@
import java.util.Arrays;
import java.util.List;
import java.util.Set;
+import java.util.function.Consumer;
import java.util.function.Predicate;
import java.util.function.Supplier;
@@ -85,6 +86,14 @@
}
@Override
+ public void accept(
+ Consumer<DexProgramClass> programClassConsumer,
+ Consumer<DexClasspathClass> classpathClassConsumer,
+ Consumer<DexLibraryClass> libraryClassConsumer) {
+ libraryClassConsumer.accept(this);
+ }
+
+ @Override
public String toString() {
return type.toString() + "(library class)";
}
diff --git a/src/main/java/com/android/tools/r8/graph/DexMember.java b/src/main/java/com/android/tools/r8/graph/DexMember.java
index 1c02f3b..9b62855 100644
--- a/src/main/java/com/android/tools/r8/graph/DexMember.java
+++ b/src/main/java/com/android/tools/r8/graph/DexMember.java
@@ -24,6 +24,8 @@
public abstract DexEncodedMember<?, ?> lookupOnClass(DexClass clazz);
+ public abstract DexClassAndMember<?, ?> lookupMemberOnClass(DexClass clazz);
+
public abstract ProgramMember<?, ?> lookupOnProgramClass(DexProgramClass clazz);
public abstract boolean match(R entry);
diff --git a/src/main/java/com/android/tools/r8/graph/DexMethod.java b/src/main/java/com/android/tools/r8/graph/DexMethod.java
index d35b1a0..29e9792 100644
--- a/src/main/java/com/android/tools/r8/graph/DexMethod.java
+++ b/src/main/java/com/android/tools/r8/graph/DexMethod.java
@@ -102,6 +102,11 @@
}
@Override
+ public DexClassAndMethod lookupMemberOnClass(DexClass clazz) {
+ return clazz != null ? clazz.lookupClassMethod(this) : null;
+ }
+
+ @Override
public ProgramMethod lookupOnProgramClass(DexProgramClass clazz) {
return clazz != null ? clazz.lookupProgramMethod(this) : null;
}
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 2a6ebf5..618ff5b 100644
--- a/src/main/java/com/android/tools/r8/graph/DexProgramClass.java
+++ b/src/main/java/com/android/tools/r8/graph/DexProgramClass.java
@@ -21,6 +21,9 @@
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.android.tools.r8.utils.TraversalContinuation;
import com.android.tools.r8.utils.structural.Ordered;
+import com.android.tools.r8.utils.structural.StructuralItem;
+import com.android.tools.r8.utils.structural.StructuralMapping;
+import com.android.tools.r8.utils.structural.StructuralSpecification;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Iterables;
import java.util.ArrayList;
@@ -37,7 +40,7 @@
import java.util.function.Supplier;
public class DexProgramClass extends DexClass
- implements ProgramDefinition, Supplier<DexProgramClass> {
+ implements ProgramDefinition, Supplier<DexProgramClass>, StructuralItem<DexProgramClass> {
@FunctionalInterface
public interface ChecksumSupplier {
@@ -144,6 +147,44 @@
synthesizedDirectlyFrom.forEach(this::addSynthesizedFrom);
}
+ @Override
+ public void accept(
+ Consumer<DexProgramClass> programClassConsumer,
+ Consumer<DexClasspathClass> classpathClassConsumer,
+ Consumer<DexLibraryClass> libraryClassConsumer) {
+ programClassConsumer.accept(this);
+ }
+
+ @Override
+ public DexProgramClass self() {
+ return this;
+ }
+
+ @Override
+ public StructuralMapping<DexProgramClass> getStructuralMapping() {
+ return DexProgramClass::specify;
+ }
+
+ private static void specify(StructuralSpecification<DexProgramClass, ?> spec) {
+ spec.withItem(c -> c.type)
+ .withItem(c -> c.superType)
+ .withItem(c -> c.interfaces)
+ .withItem(c -> c.accessFlags)
+ .withNullableItem(c -> c.sourceFile)
+ .withNullableItem(c -> c.initialClassFileVersion)
+ .withBool(c -> c.deprecated)
+ .withNullableItem(DexClass::getNestHostClassAttribute)
+ .withItemCollection(DexClass::getNestMembersClassAttributes)
+ .withItem(DexDefinition::annotations)
+ // TODO(b/158159959): Make signatures structural.
+ .withAssert(c -> c.classSignature == ClassSignature.noSignature())
+ .withItemArray(c -> c.staticFields)
+ .withItemArray(c -> c.instanceFields)
+ .withItemCollection(DexClass::allMethodsSorted)
+ // TODO(b/168584485): Synthesized-from is being removed (empty for new synthetics).
+ .withAssert(c -> c.synthesizedFrom.isEmpty());
+ }
+
public void forEachProgramField(Consumer<? super ProgramField> consumer) {
forEachField(field -> consumer.accept(new ProgramField(this, field)));
}
@@ -171,6 +212,15 @@
return Iterables.transform(directMethods(predicate), method -> new ProgramMethod(this, method));
}
+ public Iterable<ProgramMethod> virtualProgramMethods() {
+ return Iterables.transform(virtualMethods(), method -> new ProgramMethod(this, method));
+ }
+
+ public Iterable<ProgramMethod> virtualProgramMethods(Predicate<DexEncodedMethod> predicate) {
+ return Iterables.transform(
+ virtualMethods(predicate), method -> new ProgramMethod(this, method));
+ }
+
public Iterable<ProgramMethod> programInstanceInitializers() {
return directProgramMethods(DexEncodedMethod::isInstanceInitializer);
}
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 27284ea..56f9a92 100644
--- a/src/main/java/com/android/tools/r8/graph/DexType.java
+++ b/src/main/java/com/android/tools/r8/graph/DexType.java
@@ -8,7 +8,6 @@
import static com.android.tools.r8.ir.desugar.DesugaredLibraryWrapperSynthesizer.VIVIFIED_TYPE_WRAPPER_SUFFIX;
import static com.android.tools.r8.ir.desugar.InterfaceMethodRewriter.COMPANION_CLASS_NAME_SUFFIX;
import static com.android.tools.r8.ir.desugar.InterfaceMethodRewriter.EMULATE_LIBRARY_CLASS_NAME_SUFFIX;
-import static com.android.tools.r8.ir.desugar.LambdaRewriter.LAMBDA_CLASS_NAME_PREFIX;
import static com.android.tools.r8.ir.desugar.LambdaRewriter.LAMBDA_GROUP_CLASS_NAME_PREFIX;
import static com.android.tools.r8.ir.optimize.enums.UnboxedEnumMemberRelocator.ENUM_UNBOXING_UTILITY_CLASS_SUFFIX;
@@ -16,11 +15,9 @@
import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.horizontalclassmerging.SyntheticArgumentClass;
import com.android.tools.r8.ir.desugar.DesugaredLibraryRetargeter;
-import com.android.tools.r8.ir.desugar.NestBasedAccessDesugaring;
-import com.android.tools.r8.ir.desugar.TwrCloseResourceRewriter;
-import com.android.tools.r8.ir.optimize.ServiceLoaderRewriter;
+import com.android.tools.r8.ir.desugar.nest.NestBasedAccessDesugaring;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
-import com.android.tools.r8.synthesis.SyntheticItems;
+import com.android.tools.r8.synthesis.SyntheticNaming;
import com.android.tools.r8.utils.DescriptorUtils;
import com.android.tools.r8.utils.InternalOptions.OutlineOptions;
import com.android.tools.r8.utils.structural.CompareToVisitor;
@@ -41,7 +38,13 @@
// Bundletool is merging classes that may originate from a build with an old version of R8.
// Allow merging of classes that use names from older versions of R8.
private static List<String> OLD_SYNTHESIZED_NAMES =
- ImmutableList.of("$r8$backportedMethods$utility", "$r8$java8methods$utility", "$-DC");
+ ImmutableList.of(
+ "$r8$backportedMethods$utility",
+ "$r8$java8methods$utility",
+ "$r8$twr$utility",
+ "$-DC",
+ "$$ServiceLoaderMethods",
+ "-$$Lambda$");
public final DexString descriptor;
private String toStringCache = null;
@@ -172,6 +175,10 @@
classConsumer.accept(this, arg);
}
+ public String getTypeName() {
+ return toSourceString();
+ }
+
@Override
public String toSourceString() {
if (toStringCache == null) {
@@ -302,12 +309,6 @@
}
// TODO(b/158159959): Remove usage of name-based identification.
- public boolean isD8R8SynthesizedLambdaClassType() {
- String name = toSourceString();
- return name.contains(LAMBDA_CLASS_NAME_PREFIX);
- }
-
- // TODO(b/158159959): Remove usage of name-based identification.
public boolean isD8R8SynthesizedClassType() {
String name = toSourceString();
// The synthesized classes listed here must always be unique to a program context and thus
@@ -318,8 +319,7 @@
|| name.contains(ENUM_UNBOXING_UTILITY_CLASS_SUFFIX)
|| name.contains(SyntheticArgumentClass.SYNTHETIC_CLASS_SUFFIX)
// New and hygienic synthesis infrastructure.
- || name.contains(SyntheticItems.INTERNAL_SYNTHETIC_CLASS_SEPARATOR)
- || name.contains(SyntheticItems.EXTERNAL_SYNTHETIC_CLASS_SEPARATOR)
+ || SyntheticNaming.isSyntheticName(name)
// Only generated in core lib.
|| name.contains(EMULATE_LIBRARY_CLASS_NAME_SUFFIX)
|| name.contains(TYPE_WRAPPER_SUFFIX)
@@ -337,12 +337,9 @@
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_CLASS_NAME_PREFIX) // Could collide.
- || name.contains(LAMBDA_GROUP_CLASS_NAME_PREFIX) // Could collide.
+ return name.contains(LAMBDA_GROUP_CLASS_NAME_PREFIX) // Could collide.
|| name.contains(OutlineOptions.CLASS_NAME) // Global singleton.
- || name.contains(TwrCloseResourceRewriter.UTILITY_CLASS_NAME) // Global singleton.
- || name.contains(NestBasedAccessDesugaring.NEST_CONSTRUCTOR_NAME) // Global singleton.
- || name.contains(ServiceLoaderRewriter.SERVICE_LOADER_CLASS_NAME); // Global singleton.
+ || name.contains(NestBasedAccessDesugaring.NEST_CONSTRUCTOR_NAME); // Global singleton.
}
private boolean oldSynthesizedName(String name) {
diff --git a/src/main/java/com/android/tools/r8/graph/DexTypeList.java b/src/main/java/com/android/tools/r8/graph/DexTypeList.java
index 076322a..07410c0 100644
--- a/src/main/java/com/android/tools/r8/graph/DexTypeList.java
+++ b/src/main/java/com/android/tools/r8/graph/DexTypeList.java
@@ -54,6 +54,10 @@
return values.isEmpty() ? DexTypeList.empty() : new DexTypeList(values);
}
+ public DexType get(int index) {
+ return values[index];
+ }
+
public DexTypeList keepIf(Predicate<DexType> predicate) {
DexType[] filtered = ArrayUtils.filter(DexType[].class, values, predicate);
if (filtered != values) {
diff --git a/src/main/java/com/android/tools/r8/graph/DirectMappedDexApplication.java b/src/main/java/com/android/tools/r8/graph/DirectMappedDexApplication.java
index 5a7d18f..4f38fb2 100644
--- a/src/main/java/com/android/tools/r8/graph/DirectMappedDexApplication.java
+++ b/src/main/java/com/android/tools/r8/graph/DirectMappedDexApplication.java
@@ -63,11 +63,12 @@
return programClasses;
}
- public Collection<DexLibraryClass> libraryClasses() {
+ public List<DexLibraryClass> libraryClasses() {
return libraryClasses;
}
- public Collection<DexClasspathClass> classpathClasses() {
+ @Override
+ public List<DexClasspathClass> classpathClasses() {
return classpathClasses;
}
@@ -181,21 +182,19 @@
public static class Builder extends DexApplication.Builder<Builder> {
private ImmutableList<DexLibraryClass> libraryClasses;
- private ImmutableList<DexClasspathClass> classpathClasses;
Builder(LazyLoadedDexApplication application) {
super(application);
// As a side-effect, this will force-load all classes.
AllClasses allClasses = application.loadAllClasses();
libraryClasses = allClasses.getLibraryClasses();
- classpathClasses = allClasses.getClasspathClasses();
replaceProgramClasses(allClasses.getProgramClasses());
+ replaceClasspathClasses(allClasses.getClasspathClasses());
}
private Builder(DirectMappedDexApplication application) {
super(application);
libraryClasses = application.libraryClasses;
- classpathClasses = application.classpathClasses;
}
@Override
@@ -208,20 +207,6 @@
return self();
}
- public Builder replaceClasspathClasses(Collection<DexClasspathClass> classpathClasses) {
- this.classpathClasses = ImmutableList.copyOf(classpathClasses);
- return self();
- }
-
- public Builder addClasspathClasses(Collection<DexClasspathClass> classes) {
- classpathClasses =
- ImmutableList.<DexClasspathClass>builder()
- .addAll(classpathClasses)
- .addAll(classes)
- .build();
- return self();
- }
-
public Builder addLibraryClasses(Collection<DexLibraryClass> classes) {
libraryClasses =
ImmutableList.<DexLibraryClass>builder().addAll(libraryClasses).addAll(classes).build();
@@ -234,17 +219,17 @@
// TODO(zerny): Consider not rebuilding the map if no program classes are added.
Map<DexType, DexClass> allClasses =
new IdentityHashMap<>(
- programClasses.size() + classpathClasses.size() + libraryClasses.size());
+ getProgramClasses().size() + getClasspathClasses().size() + libraryClasses.size());
// Note: writing classes in reverse priority order, so a duplicate will be correctly ordered.
// There should never be duplicates and that is asserted in the addAll subroutine.
addAll(allClasses, libraryClasses);
- addAll(allClasses, classpathClasses);
- addAll(allClasses, programClasses);
+ addAll(allClasses, getClasspathClasses());
+ addAll(allClasses, getProgramClasses());
return new DirectMappedDexApplication(
proguardMap,
allClasses,
- ImmutableList.copyOf(programClasses),
- classpathClasses,
+ ImmutableList.copyOf(getProgramClasses()),
+ ImmutableList.copyOf(getClasspathClasses()),
libraryClasses,
ImmutableList.copyOf(dataResourceProviders),
options,
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 0e3d784..ca9a57a 100644
--- a/src/main/java/com/android/tools/r8/graph/GraphLens.java
+++ b/src/main/java/com/android/tools/r8/graph/GraphLens.java
@@ -5,7 +5,6 @@
import static com.android.tools.r8.graph.DexProgramClass.asProgramClassOrNull;
import static com.android.tools.r8.horizontalclassmerging.ClassMerger.CLASS_ID_FIELD_NAME;
-import static com.android.tools.r8.ir.desugar.LambdaRewriter.LAMBDA_CLASS_NAME_PREFIX;
import static com.android.tools.r8.ir.desugar.LambdaRewriter.LAMBDA_INSTANCE_FIELD_NAME;
import com.android.tools.r8.errors.Unreachable;
@@ -573,6 +572,14 @@
return builder.build();
}
+ public ImmutableSet<DexField> rewriteFields(Set<DexField> fields) {
+ ImmutableSet.Builder<DexField> builder = ImmutableSet.builder();
+ for (DexField field : fields) {
+ builder.add(getRenamedFieldSignature(field));
+ }
+ return builder.build();
+ }
+
public <T> ImmutableMap<DexField, T> rewriteFieldKeys(Map<DexField, T> map) {
ImmutableMap.Builder<DexField, T> builder = ImmutableMap.builder();
map.forEach((field, value) -> builder.put(getRenamedFieldSignature(field), value));
@@ -620,7 +627,7 @@
// that they can be mapped back to the original program.
DexField originalField = getOriginalFieldSignature(field.getReference());
assert originalFields.contains(originalField)
- || isD8R8SynthesizedField(originalField, dexItemFactory)
+ || isD8R8SynthesizedField(originalField, appView)
: "Unable to map field `"
+ field.getReference().toSourceString()
+ "` back to original program";
@@ -638,16 +645,16 @@
return true;
}
- private boolean isD8R8SynthesizedField(DexField field, DexItemFactory dexItemFactory) {
+ private boolean isD8R8SynthesizedField(DexField field, AppView<?> appView) {
// TODO(b/167947782): Should be a general check to see if the field is D8/R8 synthesized
// instead of relying on field names.
- if (field.match(dexItemFactory.objectMembers.clinitField)) {
+ if (field.match(appView.dexItemFactory().objectMembers.clinitField)) {
return true;
}
if (field.getName().toSourceString().equals(CLASS_ID_FIELD_NAME)) {
return true;
}
- if (field.getHolderType().toSourceString().contains(LAMBDA_CLASS_NAME_PREFIX)
+ if (appView.getSyntheticItems().isSyntheticClass(field.getHolderType())
&& field.getName().toSourceString().equals(LAMBDA_INSTANCE_FIELD_NAME)) {
return true;
}
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 88596f9..40b6445 100644
--- a/src/main/java/com/android/tools/r8/graph/JarClassFileReader.java
+++ b/src/main/java/com/android/tools/r8/graph/JarClassFileReader.java
@@ -817,8 +817,7 @@
private boolean classRequiresCode() {
return parent.classKind == ClassKind.PROGRAM
- || (parent.application.options.enableNestBasedAccessDesugaring
- && !parent.application.options.canUseNestBasedAccess()
+ || (!parent.application.options.canUseNestBasedAccess()
&& parent.classKind == ClassKind.CLASSPATH
&& parent.isInANest());
}
diff --git a/src/main/java/com/android/tools/r8/graph/LazyCfCode.java b/src/main/java/com/android/tools/r8/graph/LazyCfCode.java
index 6a70cdd..ba22b16 100644
--- a/src/main/java/com/android/tools/r8/graph/LazyCfCode.java
+++ b/src/main/java/com/android/tools/r8/graph/LazyCfCode.java
@@ -105,7 +105,7 @@
private final Origin origin;
private JarApplicationReader application;
private CfCode code;
- protected ReparseContext context;
+ private ReparseContext context;
private boolean reachabilitySensitive = false;
public void markReachabilitySensitive() {
diff --git a/src/main/java/com/android/tools/r8/graph/LazyLoadedDexApplication.java b/src/main/java/com/android/tools/r8/graph/LazyLoadedDexApplication.java
index ce5ff83..ba240f6 100644
--- a/src/main/java/com/android/tools/r8/graph/LazyLoadedDexApplication.java
+++ b/src/main/java/com/android/tools/r8/graph/LazyLoadedDexApplication.java
@@ -54,6 +54,12 @@
}
@Override
+ List<DexClasspathClass> classpathClasses() {
+ classpathClasses.forceLoad(t -> true);
+ return classpathClasses.getAllClasses();
+ }
+
+ @Override
public DexClass definitionFor(DexType type) {
assert type.isClassType() : "Cannot lookup definition for type: " + type;
DexClass clazz = null;
@@ -199,7 +205,7 @@
Builder(ProgramClassConflictResolver resolver, InternalOptions options, Timing timing) {
super(options, timing);
this.resolver = resolver;
- this.classpathClasses = null;
+ this.classpathClasses = ClasspathClassCollection.empty();
this.libraryClasses = null;
}
@@ -229,7 +235,7 @@
public LazyLoadedDexApplication build() {
return new LazyLoadedDexApplication(
proguardMap,
- ProgramClassCollection.create(programClasses, resolver),
+ ProgramClassCollection.create(getProgramClasses(), resolver),
ImmutableList.copyOf(dataResourceProviders),
classpathClasses,
libraryClasses,
diff --git a/src/main/java/com/android/tools/r8/graph/LibraryField.java b/src/main/java/com/android/tools/r8/graph/LibraryField.java
new file mode 100644
index 0000000..49ed565
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/graph/LibraryField.java
@@ -0,0 +1,35 @@
+// 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 class LibraryField extends DexClassAndField
+ implements LibraryMember<DexEncodedField, DexField> {
+
+ public LibraryField(DexLibraryClass holder, DexEncodedField field) {
+ super(holder, field);
+ }
+
+ @Override
+ public DexLibraryClass getHolder() {
+ DexClass holder = super.getHolder();
+ assert holder.isLibraryClass();
+ return holder.asLibraryClass();
+ }
+
+ @Override
+ public boolean isLibraryField() {
+ return true;
+ }
+
+ @Override
+ public LibraryField asLibraryField() {
+ return this;
+ }
+
+ @Override
+ public boolean isLibraryMember() {
+ return true;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/graph/LibraryMember.java b/src/main/java/com/android/tools/r8/graph/LibraryMember.java
new file mode 100644
index 0000000..880da78
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/graph/LibraryMember.java
@@ -0,0 +1,14 @@
+// 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 LibraryMember<D extends DexEncodedMember<D, R>, R extends DexMember<D, R>> {
+
+ D getDefinition();
+
+ DexLibraryClass getHolder();
+
+ DexType getHolderType();
+}
diff --git a/src/main/java/com/android/tools/r8/graph/LibraryMethod.java b/src/main/java/com/android/tools/r8/graph/LibraryMethod.java
index c06ebab..29cdc41 100644
--- a/src/main/java/com/android/tools/r8/graph/LibraryMethod.java
+++ b/src/main/java/com/android/tools/r8/graph/LibraryMethod.java
@@ -4,13 +4,26 @@
package com.android.tools.r8.graph;
/** Type representing a method definition from the library and its holder. */
-public final class LibraryMethod extends DexClassAndMethod {
+public final class LibraryMethod extends DexClassAndMethod
+ implements LibraryMember<DexEncodedMethod, DexMethod> {
public LibraryMethod(DexLibraryClass holder, DexEncodedMethod method) {
super(holder, method);
}
@Override
+ public DexLibraryClass getHolder() {
+ DexClass holder = super.getHolder();
+ assert holder.isLibraryClass();
+ return holder.asLibraryClass();
+ }
+
+ @Override
+ public boolean isLibraryMember() {
+ return true;
+ }
+
+ @Override
public boolean isLibraryMethod() {
return true;
}
@@ -19,11 +32,4 @@
public LibraryMethod asLibraryMethod() {
return this;
}
-
- @Override
- public DexLibraryClass getHolder() {
- DexClass holder = super.getHolder();
- assert holder.isLibraryClass();
- return holder.asLibraryClass();
- }
}
diff --git a/src/main/java/com/android/tools/r8/graph/MethodCollection.java b/src/main/java/com/android/tools/r8/graph/MethodCollection.java
index 9339630..ccf33aa 100644
--- a/src/main/java/com/android/tools/r8/graph/MethodCollection.java
+++ b/src/main/java/com/android/tools/r8/graph/MethodCollection.java
@@ -130,7 +130,7 @@
return backing.methods();
}
- public Iterable<DexEncodedMethod> methods(Predicate<DexEncodedMethod> predicate) {
+ public Iterable<DexEncodedMethod> methods(Predicate<? super DexEncodedMethod> predicate) {
return IterableUtils.filter(methods(), predicate);
}
diff --git a/src/main/java/com/android/tools/r8/graph/MethodMapBacking.java b/src/main/java/com/android/tools/r8/graph/MethodMapBacking.java
index c6f7a5f..f3385af 100644
--- a/src/main/java/com/android/tools/r8/graph/MethodMapBacking.java
+++ b/src/main/java/com/android/tools/r8/graph/MethodMapBacking.java
@@ -34,7 +34,7 @@
}
public static MethodMapBacking createSorted() {
- Comparator<Wrapper<DexMethod>> comparator = (x, y) -> x.get().compareTo(y.get());
+ Comparator<Wrapper<DexMethod>> comparator = Comparator.comparing(Wrapper::get);
return new MethodMapBacking(new Object2ReferenceRBTreeMap<>(comparator));
}
diff --git a/src/main/java/com/android/tools/r8/graph/NestHostClassAttribute.java b/src/main/java/com/android/tools/r8/graph/NestHostClassAttribute.java
index e007f4e..c9969a6 100644
--- a/src/main/java/com/android/tools/r8/graph/NestHostClassAttribute.java
+++ b/src/main/java/com/android/tools/r8/graph/NestHostClassAttribute.java
@@ -5,12 +5,19 @@
package com.android.tools.r8.graph;
import com.android.tools.r8.naming.NamingLens;
+import com.android.tools.r8.utils.structural.StructuralItem;
+import com.android.tools.r8.utils.structural.StructuralMapping;
+import com.android.tools.r8.utils.structural.StructuralSpecification;
import org.objectweb.asm.ClassWriter;
-public class NestHostClassAttribute {
+public class NestHostClassAttribute implements StructuralItem<NestHostClassAttribute> {
private final DexType nestHost;
+ private static void specify(StructuralSpecification<NestHostClassAttribute, ?> spec) {
+ spec.withItem(a -> a.nestHost);
+ }
+
public NestHostClassAttribute(DexType nestHost) {
this.nestHost = nestHost;
}
@@ -27,4 +34,14 @@
assert nestHost != null;
writer.visitNestHost(lens.lookupInternalName(nestHost));
}
+
+ @Override
+ public NestHostClassAttribute self() {
+ return this;
+ }
+
+ @Override
+ public StructuralMapping<NestHostClassAttribute> getStructuralMapping() {
+ return NestHostClassAttribute::specify;
+ }
}
diff --git a/src/main/java/com/android/tools/r8/graph/NestMemberClassAttribute.java b/src/main/java/com/android/tools/r8/graph/NestMemberClassAttribute.java
index f9d1a35..0d7c19d 100644
--- a/src/main/java/com/android/tools/r8/graph/NestMemberClassAttribute.java
+++ b/src/main/java/com/android/tools/r8/graph/NestMemberClassAttribute.java
@@ -5,14 +5,21 @@
package com.android.tools.r8.graph;
import com.android.tools.r8.naming.NamingLens;
+import com.android.tools.r8.utils.structural.StructuralItem;
+import com.android.tools.r8.utils.structural.StructuralMapping;
+import com.android.tools.r8.utils.structural.StructuralSpecification;
import java.util.Collections;
import java.util.List;
import org.objectweb.asm.ClassWriter;
-public class NestMemberClassAttribute {
+public class NestMemberClassAttribute implements StructuralItem<NestMemberClassAttribute> {
private final DexType nestMember;
+ private static void specify(StructuralSpecification<NestMemberClassAttribute, ?> spec) {
+ spec.withItem(a -> a.nestMember);
+ }
+
public NestMemberClassAttribute(DexType nestMember) {
this.nestMember = nestMember;
}
@@ -29,4 +36,14 @@
assert nestMember != null;
writer.visitNestMember(lens.lookupInternalName(nestMember));
}
+
+ @Override
+ public NestMemberClassAttribute self() {
+ return this;
+ }
+
+ @Override
+ public StructuralMapping<NestMemberClassAttribute> getStructuralMapping() {
+ return NestMemberClassAttribute::specify;
+ }
}
diff --git a/src/main/java/com/android/tools/r8/graph/ResolutionResult.java b/src/main/java/com/android/tools/r8/graph/ResolutionResult.java
index edb5e46..3356777 100644
--- a/src/main/java/com/android/tools/r8/graph/ResolutionResult.java
+++ b/src/main/java/com/android/tools/r8/graph/ResolutionResult.java
@@ -567,18 +567,14 @@
public LookupTarget lookupVirtualDispatchTarget(
LambdaDescriptor lambdaInstance, AppInfoWithClassHierarchy appInfo) {
if (lambdaInstance.getMainMethod().match(resolvedMethod)) {
- DexMethod method = lambdaInstance.implHandle.asMethod();
- DexClass holder = appInfo.definitionForHolder(method);
- if (holder == null) {
- assert false;
- return null;
- }
- DexEncodedMethod definition = holder.lookupMethod(method);
- if (definition == null) {
+ DexMethod methodReference = lambdaInstance.implHandle.asMethod();
+ DexClass holder = appInfo.definitionForHolder(methodReference);
+ DexClassAndMethod method = methodReference.lookupMemberOnClass(holder);
+ if (method == null) {
// The targeted method might not exist, eg, Throwable.addSuppressed in an old library.
return null;
}
- return new LookupLambdaTarget(lambdaInstance, DexClassAndMethod.create(holder, definition));
+ return new LookupLambdaTarget(lambdaInstance, method);
}
return lookupMaximallySpecificDispatchTarget(lambdaInstance, appInfo);
}
diff --git a/src/main/java/com/android/tools/r8/graph/TreeFixerBase.java b/src/main/java/com/android/tools/r8/graph/TreeFixerBase.java
index a10db7147..89d0bd7 100644
--- a/src/main/java/com/android/tools/r8/graph/TreeFixerBase.java
+++ b/src/main/java/com/android/tools/r8/graph/TreeFixerBase.java
@@ -4,6 +4,7 @@
package com.android.tools.r8.graph;
+import com.android.tools.r8.shaking.AppInfoWithLiveness;
import java.util.ArrayList;
import java.util.Collection;
import java.util.IdentityHashMap;
@@ -15,7 +16,7 @@
private final AppView<?> appView;
private final DexItemFactory dexItemFactory;
- private Map<DexType, DexProgramClass> newProgramClasses = null;
+ private final Map<DexType, DexProgramClass> programClassCache = new IdentityHashMap<>();
private final Map<DexType, DexProgramClass> synthesizedFromClasses = new IdentityHashMap<>();
private final Map<DexProto, DexProto> protoFixupCache = new IdentityHashMap<>();
@@ -46,6 +47,34 @@
return to;
}
+ /** Rewrite missing references */
+ public void recordFailedResolutionChanges() {
+ // In order for optimizations to correctly rewrite field and method references that do not
+ // resolve, we create a mapping from each failed resolution target to its reference reference.
+ if (!appView.appInfo().hasLiveness()) {
+ return;
+ }
+ AppInfoWithLiveness appInfoWithLiveness = appView.appInfo().withLiveness();
+ appInfoWithLiveness
+ .getFailedFieldResolutionTargets()
+ .forEach(
+ field -> {
+ DexField fixedUpField = fixupFieldReference(field);
+ if (field != fixedUpField) {
+ recordFieldChange(field, fixedUpField);
+ }
+ });
+ appInfoWithLiveness
+ .getFailedMethodResolutionTargets()
+ .forEach(
+ method -> {
+ DexMethod fixedUpMethod = fixupMethodReference(method);
+ if (method != fixedUpMethod) {
+ recordMethodChange(method, fixedUpMethod);
+ }
+ });
+ }
+
/** Callback to allow custom handling when an encoded method changes. */
public DexEncodedMethod recordMethodChange(DexEncodedMethod from, DexEncodedMethod to) {
recordMethodChange(from.method, to.method);
@@ -53,13 +82,13 @@
}
/** Fixup a collection of classes. */
- public Collection<DexProgramClass> fixupClasses(Collection<DexProgramClass> classes) {
- assert newProgramClasses == null;
- newProgramClasses = new IdentityHashMap<>();
+ public List<DexProgramClass> fixupClasses(Collection<DexProgramClass> classes) {
+ List<DexProgramClass> newProgramClasses = new ArrayList<>();
for (DexProgramClass clazz : classes) {
- newProgramClasses.computeIfAbsent(clazz.getType(), ignore -> fixupClass(clazz));
+ newProgramClasses.add(
+ programClassCache.computeIfAbsent(clazz.getType(), ignore -> fixupClass(clazz)));
}
- return newProgramClasses.values();
+ return newProgramClasses;
}
// Should remain private as the correctness of the fixup requires the lazy 'newProgramClasses'.
@@ -70,7 +99,7 @@
clazz.getOriginKind(),
clazz.getOrigin(),
clazz.getAccessFlags(),
- fixupType(clazz.superType),
+ clazz.superType == null ? null : fixupType(clazz.superType),
fixupTypeList(clazz.interfaces),
clazz.getSourceFile(),
fixupNestHost(clazz.getNestHostClassAttribute()),
@@ -250,7 +279,6 @@
// Should remain private as its correctness relies on the setup of 'newProgramClasses'.
private Collection<DexProgramClass> fixupSynthesizedFrom(
Collection<DexProgramClass> synthesizedFrom) {
- assert newProgramClasses != null;
if (synthesizedFrom.isEmpty()) {
return synthesizedFrom;
}
@@ -261,7 +289,7 @@
// is no longer in the application?
Map<DexType, DexProgramClass> classes =
appView.appInfo().definitionForWithoutExistenceAssert(clazz.getType()) != null
- ? newProgramClasses
+ ? programClassCache
: synthesizedFromClasses;
DexProgramClass newClass =
classes.computeIfAbsent(clazz.getType(), ignore -> fixupClass(clazz));
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/ClassMerger.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/ClassMerger.java
index 0a6a9b9..11001de 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/ClassMerger.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/ClassMerger.java
@@ -172,7 +172,7 @@
return dexItemFactory.createFreshMethodName(
method.getDefinition().method.name.toSourceString(),
method.getHolderType(),
- method.getDefinition().proto(),
+ method.getDefinition().getProto(),
group.getTarget().getType(),
classMethodsBuilder::isFresh);
}
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/ClassMethodsBuilder.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/ClassMethodsBuilder.java
index 13c01eb..b87c6ad 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/ClassMethodsBuilder.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/ClassMethodsBuilder.java
@@ -35,9 +35,9 @@
public void setClassMethods(DexProgramClass clazz) {
assert virtualMethods.stream().allMatch(method -> method.getHolderType() == clazz.type);
- assert virtualMethods.stream().allMatch(method -> method.belongsToVirtualPool());
+ assert virtualMethods.stream().allMatch(DexEncodedMethod::belongsToVirtualPool);
assert directMethods.stream().allMatch(method -> method.getHolderType() == clazz.type);
- assert directMethods.stream().allMatch(method -> method.belongsToDirectPool());
+ assert directMethods.stream().allMatch(DexEncodedMethod::belongsToDirectPool);
clazz.setVirtualMethods(virtualMethods);
clazz.setDirectMethods(directMethods);
}
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/ConstructorMerger.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/ConstructorMerger.java
index b4e9887..13673fa 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/ConstructorMerger.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/ConstructorMerger.java
@@ -45,7 +45,7 @@
// Constructors should not be empty and all constructors should have the same prototype.
assert !constructors.isEmpty();
- assert constructors.stream().map(DexEncodedMethod::proto).distinct().count() == 1;
+ assert constructors.stream().map(DexEncodedMethod::getProto).distinct().count() == 1;
this.dexItemFactory = appView.dexItemFactory();
}
@@ -113,7 +113,7 @@
dexItemFactory.createFreshMethodName(
TEMPORARY_INSTANCE_INITIALIZER_PREFIX,
constructor.getHolderType(),
- constructor.proto(),
+ constructor.getProto(),
group.getTarget().getType(),
classMethodsBuilder::isFresh);
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/FieldAccessAnalysis.java b/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/FieldAccessAnalysis.java
index 015b182..2b5e70f 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/FieldAccessAnalysis.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/FieldAccessAnalysis.java
@@ -62,7 +62,7 @@
public void recordFieldAccesses(
IRCode code, OptimizationFeedback feedback, MethodProcessor methodProcessor) {
- if (!methodProcessor.isPrimary()) {
+ if (!methodProcessor.isPrimaryMethodProcessor()) {
return;
}
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/StaticFieldValueAnalysis.java b/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/StaticFieldValueAnalysis.java
index 281215f..fc01602 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/StaticFieldValueAnalysis.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/StaticFieldValueAnalysis.java
@@ -12,9 +12,6 @@
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.DexField;
-import com.android.tools.r8.graph.DexItemFactory;
-import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.DexValue;
import com.android.tools.r8.graph.DexValue.DexValueNull;
import com.android.tools.r8.ir.analysis.type.ClassTypeElement;
@@ -26,6 +23,7 @@
import com.android.tools.r8.ir.analysis.value.NullOrAbstractValue;
import com.android.tools.r8.ir.analysis.value.ObjectState;
import com.android.tools.r8.ir.analysis.value.SingleFieldValue;
+import com.android.tools.r8.ir.analysis.value.UnknownValue;
import com.android.tools.r8.ir.code.ArrayPut;
import com.android.tools.r8.ir.code.FieldInstruction;
import com.android.tools.r8.ir.code.IRCode;
@@ -40,10 +38,13 @@
import com.android.tools.r8.ir.optimize.info.field.InstanceFieldInitializationInfoCollection;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.android.tools.r8.utils.Timing;
+import java.util.IdentityHashMap;
+import java.util.Map;
public class StaticFieldValueAnalysis extends FieldValueAnalysis {
private final StaticFieldValues.Builder builder;
+ private final Map<Value, AbstractValue> computedValues = new IdentityHashMap<>();
private StaticFieldValueAnalysis(
AppView<AppInfoWithLiveness> appView, IRCode code, OptimizationFeedback feedback) {
@@ -63,7 +64,7 @@
timing.begin("Analyze class initializer");
StaticFieldValues result =
new StaticFieldValueAnalysis(appView.withLiveness(), code, feedback)
- .analyze(classInitializerDefaultsResult, code.context().getHolderType());
+ .analyze(classInitializerDefaultsResult);
timing.end();
return result;
}
@@ -78,8 +79,7 @@
return this;
}
- StaticFieldValues analyze(
- ClassInitializerDefaultsResult classInitializerDefaultsResult, DexType holderType) {
+ StaticFieldValues analyze(ClassInitializerDefaultsResult classInitializerDefaultsResult) {
computeFieldOptimizationInfo(classInitializerDefaultsResult);
return builder.build();
}
@@ -218,16 +218,41 @@
return null;
}
assert !value.hasAliasedValue();
- if (isEnumValuesArray(value)) {
+ if (value.isPhi()) {
+ return null;
+ }
+ if (value.definition.isNewArrayEmpty()) {
return computeSingleEnumFieldValueForValuesArray(value);
}
- return computeSingleEnumFieldValueForInstance(value);
+ if (value.definition.isNewInstance()) {
+ return computeSingleEnumFieldValueForInstance(value);
+ }
+ return null;
}
private SingleFieldValue computeSingleEnumFieldValueForValuesArray(Value value) {
- if (!value.isDefinedByInstructionSatisfying(Instruction::isNewArrayEmpty)) {
+ if (!value.definition.isNewArrayEmpty()) {
return null;
}
+ AbstractValue valuesValue = computedValues.get(value);
+ if (valuesValue != null) {
+ // This implicitely answers null if the value could not get computed.
+ if (valuesValue.isSingleFieldValue()) {
+ SingleFieldValue fieldValue = valuesValue.asSingleFieldValue();
+ if (fieldValue.getState().isEnumValuesObjectState()) {
+ return fieldValue;
+ }
+ }
+ return null;
+ }
+ SingleFieldValue singleFieldValue = internalComputeSingleEnumFieldValueForValuesArray(value);
+ computedValues.put(
+ value, singleFieldValue == null ? UnknownValue.getInstance() : singleFieldValue);
+ return singleFieldValue;
+ }
+
+ private SingleFieldValue internalComputeSingleEnumFieldValueForValuesArray(Value value) {
+ assert value.isDefinedByInstructionSatisfying(Instruction::isNewArrayEmpty);
NewArrayEmpty newArrayEmpty = value.definition.asNewArrayEmpty();
if (newArrayEmpty.type.toBaseType(appView.dexItemFactory()) != context.getHolder().type) {
@@ -267,7 +292,9 @@
// We need the state of all fields for the analysis to be valuable.
return null;
}
- assert verifyValuesArrayIndexMatchesOrdinal(index, objectState);
+ if (!valuesArrayIndexMatchesOrdinal(index, objectState)) {
+ return null;
+ }
if (valuesState[index] != null) {
return null;
}
@@ -326,24 +353,25 @@
return ObjectState.empty();
}
- private boolean verifyValuesArrayIndexMatchesOrdinal(int ordinal, ObjectState objectState) {
+ private boolean valuesArrayIndexMatchesOrdinal(int ordinal, ObjectState objectState) {
DexEncodedField ordinalField =
appView
.appInfo()
.resolveField(appView.dexItemFactory().enumMembers.ordinalField, context)
.getResolvedField();
- assert ordinalField != null;
+ if (ordinalField == null) {
+ return false;
+ }
AbstractValue ordinalState = objectState.getAbstractFieldValue(ordinalField);
- assert ordinalState != null;
- assert ordinalState.isSingleNumberValue();
- assert ordinalState.asSingleNumberValue().getIntValue() == ordinal;
- return true;
+ if (ordinalState == null || !ordinalState.isSingleNumberValue()) {
+ return false;
+ }
+ int intValue = ordinalState.asSingleNumberValue().getIntValue();
+ return intValue == ordinal;
}
private SingleFieldValue computeSingleEnumFieldValueForInstance(Value value) {
- if (!value.isDefinedByInstructionSatisfying(Instruction::isNewInstance)) {
- return null;
- }
+ assert value.isDefinedByInstructionSatisfying(Instruction::isNewInstance);
NewInstance newInstance = value.definition.asNewInstance();
// Some enums have direct subclasses, and the subclass is instantiated here.
@@ -459,30 +487,7 @@
}
private boolean isEnumValuesArray(Value value) {
- assert context.getHolder().isEnum();
- DexItemFactory dexItemFactory = appView.dexItemFactory();
- DexField valuesField =
- dexItemFactory.createField(
- context.getHolderType(),
- context.getHolderType().toArrayType(1, dexItemFactory),
- dexItemFactory.enumValuesFieldName);
-
- Value root = value.getAliasedValue();
- if (root.isPhi()) {
- return false;
- }
-
- Instruction definition = root.definition;
- if (definition.isNewArrayEmpty()) {
- for (Instruction user : root.aliasedUsers()) {
- if (user.isStaticPut() && user.asStaticPut().getField() == valuesField) {
- return true;
- }
- }
- } else if (definition.isStaticGet()) {
- return definition.asStaticGet().getField() == valuesField;
- }
-
- return false;
+ SingleFieldValue singleFieldValue = computeSingleEnumFieldValueForValuesArray(value);
+ return singleFieldValue != null && singleFieldValue.getState().isEnumValuesObjectState();
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/StaticFieldValues.java b/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/StaticFieldValues.java
index b2dcdcd..65e1ea3 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/StaticFieldValues.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/StaticFieldValues.java
@@ -37,17 +37,10 @@
// All the abstract values stored here may match a pinned field, using them requires therefore
// to check the field is not pinned or prove it is no longer pinned.
public static class EnumStaticFieldValues extends StaticFieldValues {
- private final ImmutableMap<DexField, AbstractValue> enumAbstractValues;
- private final DexField valuesField;
- private final AbstractValue valuesAbstractValue;
+ private final ImmutableMap<DexField, ObjectState> enumAbstractValues;
- public EnumStaticFieldValues(
- ImmutableMap<DexField, AbstractValue> enumAbstractValues,
- DexField valuesField,
- AbstractValue valuesAbstractValue) {
+ public EnumStaticFieldValues(ImmutableMap<DexField, ObjectState> enumAbstractValues) {
this.enumAbstractValues = enumAbstractValues;
- this.valuesField = valuesField;
- this.valuesAbstractValue = valuesAbstractValue;
}
static StaticFieldValues.Builder builder() {
@@ -55,33 +48,38 @@
}
public static class Builder extends StaticFieldValues.Builder {
- private final ImmutableMap.Builder<DexField, AbstractValue> enumAbstractValuesBuilder =
+ private final ImmutableMap.Builder<DexField, ObjectState> enumObjectStateBuilder =
ImmutableMap.builder();
- private DexField valuesFields;
- private AbstractValue valuesAbstractValue;
+ private AbstractValue valuesCandidateAbstractValue;
Builder() {}
@Override
public void recordStaticField(
DexEncodedField staticField, AbstractValue value, DexItemFactory factory) {
- // TODO(b/166532388): Stop relying on the values name.
- if (staticField.getName() == factory.enumValuesFieldName) {
- valuesFields = staticField.field;
- valuesAbstractValue = value;
- } else if (staticField.isEnum()) {
- enumAbstractValuesBuilder.put(staticField.field, value);
+ if (factory.enumMembers.isValuesFieldCandidate(staticField, staticField.getHolderType())) {
+ if (value.isSingleFieldValue()
+ && value.asSingleFieldValue().getState().isEnumValuesObjectState()) {
+ assert valuesCandidateAbstractValue == null
+ || valuesCandidateAbstractValue.equals(value);
+ valuesCandidateAbstractValue = value;
+ enumObjectStateBuilder.put(staticField.field, value.asSingleFieldValue().getState());
+ }
+ } else if (factory.enumMembers.isEnumField(staticField, staticField.getHolderType())) {
+ if (value.isSingleFieldValue() && !value.asSingleFieldValue().getState().isEmpty()) {
+ enumObjectStateBuilder.put(staticField.field, value.asSingleFieldValue().getState());
+ }
}
}
@Override
public StaticFieldValues build() {
- ImmutableMap<DexField, AbstractValue> enumAbstractValues =
- enumAbstractValuesBuilder.build();
- if (valuesAbstractValue == null && enumAbstractValues.isEmpty()) {
+ ImmutableMap<DexField, ObjectState> enumAbstractValues = enumObjectStateBuilder.build();
+ if (enumAbstractValues.isEmpty()) {
return EmptyStaticValues.getInstance();
}
- return new EnumStaticFieldValues(enumAbstractValues, valuesFields, valuesAbstractValue);
+ assert enumAbstractValues.values().stream().noneMatch(ObjectState::isEmpty);
+ return new EnumStaticFieldValues(enumAbstractValues);
}
}
@@ -96,20 +94,7 @@
}
public ObjectState getObjectStateForPossiblyPinnedField(DexField field) {
- AbstractValue fieldValue = enumAbstractValues.get(field);
- if (fieldValue == null || fieldValue.isZero()) {
- return null;
- }
- if (fieldValue.isSingleFieldValue()) {
- return fieldValue.asSingleFieldValue().getState();
- }
- assert fieldValue.isUnknown();
- return ObjectState.empty();
- }
-
- public AbstractValue getValuesAbstractValueForPossiblyPinnedField(DexField field) {
- assert valuesField == field || valuesAbstractValue == null;
- return valuesAbstractValue;
+ return enumAbstractValues.get(field);
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/type/ClassTypeElement.java b/src/main/java/com/android/tools/r8/ir/analysis/type/ClassTypeElement.java
index eeeea70..144f465 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/type/ClassTypeElement.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/type/ClassTypeElement.java
@@ -8,19 +8,21 @@
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexClass;
import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.ir.analysis.type.InterfaceCollection.Builder;
+import com.android.tools.r8.utils.BooleanBox;
+import com.android.tools.r8.utils.Box;
+import com.android.tools.r8.utils.OptionalBool;
+import com.android.tools.r8.utils.Pair;
import com.android.tools.r8.utils.SetUtils;
-import com.google.common.collect.ImmutableSet;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Collection;
-import java.util.Collections;
-import java.util.HashSet;
+import java.util.Comparator;
import java.util.IdentityHashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Queue;
-import java.util.Set;
import java.util.function.Function;
import java.util.stream.Collectors;
@@ -28,14 +30,14 @@
// Least upper bound of interfaces that this class type is implementing.
// Lazily computed on demand via DexItemFactory, where the canonicalized set will be maintained.
- private Set<DexType> lazyInterfaces;
+ private InterfaceCollection lazyInterfaces;
private AppView<? extends AppInfoWithClassHierarchy> appView;
// On-demand link between other nullability-variants.
private final NullabilityVariants<ClassTypeElement> variants;
private final DexType type;
public static ClassTypeElement create(
- DexType classType, Nullability nullability, Set<DexType> interfaces) {
+ DexType classType, Nullability nullability, InterfaceCollection interfaces) {
assert interfaces != null;
return NullabilityVariants.create(
nullability,
@@ -55,7 +57,7 @@
private ClassTypeElement(
DexType classType,
Nullability nullability,
- Set<DexType> interfaces,
+ InterfaceCollection interfaces,
NullabilityVariants<ClassTypeElement> variants,
AppView<? extends AppInfoWithClassHierarchy> appView) {
super(nullability);
@@ -71,7 +73,7 @@
return type;
}
- public Set<DexType> getInterfaces() {
+ public InterfaceCollection getInterfaces() {
if (lazyInterfaces == null) {
assert appView != null;
lazyInterfaces =
@@ -105,8 +107,8 @@
@Override
public boolean isBasedOnMissingClass(AppView<? extends AppInfoWithClassHierarchy> appView) {
return appView.appInfo().isMissingOrHasMissingSuperType(getClassType())
- || getInterfaces().stream()
- .anyMatch(type -> appView.appInfo().isMissingOrHasMissingSuperType(type));
+ || getInterfaces()
+ .anyMatch((iface, isKnown) -> appView.appInfo().isMissingOrHasMissingSuperType(iface));
}
@Override
@@ -131,13 +133,17 @@
builder.append(" ");
builder.append(type);
builder.append(" {");
- Set<DexType> interfaces = getInterfaces();
- if (interfaces != null) {
- List<DexType> sortedInterfaces = new ArrayList<>(interfaces);
- sortedInterfaces.sort(DexType::compareTo);
- builder.append(
- sortedInterfaces.stream().map(DexType::toString).collect(Collectors.joining(", ")));
- }
+ InterfaceCollection interfaces = getInterfaces();
+ List<Pair<DexType, Boolean>> sortedInterfaces = interfaces.getInterfaceList();
+ sortedInterfaces.sort(Comparator.comparing(Pair::getFirst));
+ builder.append(
+ sortedInterfaces.stream()
+ .map(
+ pair ->
+ pair.getSecond()
+ ? pair.getFirst().toString()
+ : ("maybe(" + pair.getFirst() + ")"))
+ .collect(Collectors.joining(", ")));
builder.append("}");
return builder.toString();
}
@@ -165,36 +171,42 @@
// For most types there will not have been a change thus we iterate without allocating a new
// set for holding modified interfaces.
- boolean hasChangedInterfaces = false;
- DexClass interfaceToClassChange = null;
- for (DexType iface : getInterfaces()) {
- DexType substitutedType = mapping.apply(iface);
- if (iface != substitutedType) {
- hasChangedInterfaces = true;
- DexClass mappedClass = appView.definitionFor(substitutedType);
- if (!mappedClass.isInterface()) {
- if (interfaceToClassChange != null && mappedClass != interfaceToClassChange) {
- throw new CompilationError(
- "More than one interface has changed to a class: "
- + interfaceToClassChange
- + " and "
- + mappedClass);
- }
- interfaceToClassChange = mappedClass;
- }
- }
- }
- if (hasChangedInterfaces) {
- if (interfaceToClassChange != null) {
- assert !interfaceToClassChange.isInterface();
+ BooleanBox hasChangedInterfaces = new BooleanBox();
+ Box<DexClass> interfaceToClassChange = new Box<>();
+ getInterfaces()
+ .forEach(
+ (iface, isKnown) -> {
+ DexType substitutedType = mapping.apply(iface);
+ if (iface != substitutedType) {
+ hasChangedInterfaces.set();
+ DexClass mappedClass = appView.definitionFor(substitutedType);
+ if (!mappedClass.isInterface()) {
+ if (interfaceToClassChange.isSet()
+ && mappedClass != interfaceToClassChange.get()) {
+ throw new CompilationError(
+ "More than one interface has changed to a class: "
+ + interfaceToClassChange.get()
+ + " and "
+ + mappedClass);
+ }
+ interfaceToClassChange.set(mappedClass);
+ }
+ }
+ });
+ if (hasChangedInterfaces.get()) {
+ if (interfaceToClassChange.isSet()) {
+ assert !interfaceToClassChange.get().isInterface();
assert type == appView.dexItemFactory().objectType;
- return create(interfaceToClassChange.type, nullability, appView);
+ return create(interfaceToClassChange.get().type, nullability, appView);
} else {
- Set<DexType> newInterfaces = new HashSet<>();
- for (DexType iface : lazyInterfaces) {
- newInterfaces.add(mapping.apply(iface));
- }
- return create(mappedType, nullability, newInterfaces);
+ Builder builder = InterfaceCollection.builder();
+ lazyInterfaces.forEach(
+ (iface, isKnown) -> {
+ DexType rewritten = mapping.apply(iface);
+ assert iface == rewritten || isKnown : "Rewritten implies program types thus known.";
+ builder.addInterface(rewritten, isKnown);
+ });
+ return create(mappedType, nullability, builder.build());
}
}
return this;
@@ -210,15 +222,15 @@
? getClassType()
: appView.dexItemFactory().objectType,
nullability,
- Collections.emptySet());
+ InterfaceCollection.empty());
}
DexType lubType =
computeLeastUpperBoundOfClasses(
appView.appInfo().withClassHierarchy(), getClassType(), other.getClassType());
- Set<DexType> c1lubItfs = getInterfaces();
- Set<DexType> c2lubItfs = other.getInterfaces();
- Set<DexType> lubItfs = null;
- if (c1lubItfs.size() == c2lubItfs.size() && c1lubItfs.containsAll(c2lubItfs)) {
+ InterfaceCollection c1lubItfs = getInterfaces();
+ InterfaceCollection c2lubItfs = other.getInterfaces();
+ InterfaceCollection lubItfs = null;
+ if (c1lubItfs.equals(c2lubItfs)) {
lubItfs = c1lubItfs;
}
if (lubItfs == null) {
@@ -228,9 +240,85 @@
return ClassTypeElement.create(lubType, nullability, lubItfs);
}
- private enum InterfaceMarker {
- LEFT,
- RIGHT
+ /**
+ * Internal marker for finding the LUB between sets of interfaces.
+ *
+ * <p>The marker is used both as the identification of which side the traversal is on and if that
+ * item is known to always be present. That use denotes a immutable use fo the marker and reuses
+ * the static constants defined below. When traversing the interface super chains each point is
+ * mapped to a mutable marking that keeps track of what paths have reached it. The mutable use is
+ * allocated with 'createEmpty' and updated with 'merge'.
+ */
+ private static class InterfaceMarker {
+
+ // Each side is tracked with a three-valued marking.
+ // Note that the value FALSE is not part of the possible three values, only:
+ // FALSE: not marked / not present.
+ // TRUE: marked and known to be present.
+ // UNKNOWN: marked and unknown if actually present.
+ private OptionalBool left;
+ private OptionalBool right;
+
+ static final InterfaceMarker LEFT_KNOWN =
+ new InterfaceMarker(OptionalBool.TRUE, OptionalBool.FALSE);
+ static final InterfaceMarker LEFT_UNKNOWN =
+ new InterfaceMarker(OptionalBool.UNKNOWN, OptionalBool.FALSE);
+ static final InterfaceMarker RIGHT_KNOWN =
+ new InterfaceMarker(OptionalBool.FALSE, OptionalBool.TRUE);
+ static final InterfaceMarker RIGHT_UNKNOWN =
+ new InterfaceMarker(OptionalBool.FALSE, OptionalBool.UNKNOWN);
+
+ static InterfaceMarker forLeft(boolean isKnown) {
+ return isKnown ? LEFT_KNOWN : LEFT_UNKNOWN;
+ }
+
+ static InterfaceMarker forRight(boolean isKnown) {
+ return isKnown ? RIGHT_KNOWN : RIGHT_UNKNOWN;
+ }
+
+ static InterfaceMarker createUnmarked() {
+ return new InterfaceMarker(OptionalBool.FALSE, OptionalBool.FALSE);
+ }
+
+ public InterfaceMarker(OptionalBool left, OptionalBool right) {
+ this.left = left;
+ this.right = right;
+ assert !isMarkedOnBothSides();
+ }
+
+ boolean isMarked() {
+ return left.isPossiblyTrue() || right.isPossiblyTrue();
+ }
+
+ boolean isMarkedOnBothSides() {
+ return left.isPossiblyTrue() && right.isPossiblyTrue();
+ }
+
+ static OptionalBool knownIfAnyIsKnown(OptionalBool v1, OptionalBool v2) {
+ assert v1.isPossiblyTrue() || v2.isPossiblyTrue();
+ return v1.isTrue() || v2.isTrue() ? OptionalBool.TRUE : OptionalBool.UNKNOWN;
+ }
+
+ boolean knownIfBothAreKnown() {
+ assert isMarkedOnBothSides();
+ return left.isTrue() && right.isTrue();
+ }
+
+ boolean merge(InterfaceMarker marker) {
+ assert marker.isMarked();
+ assert !marker.isMarkedOnBothSides();
+ if (marker.left.isPossiblyTrue()) {
+ OptionalBool oldLeft = left;
+ left = knownIfAnyIsKnown(left, marker.left);
+ // Only continue if the other side is absent and this side changed.
+ return right.isFalse() && left != oldLeft;
+ } else {
+ OptionalBool oldRight = right;
+ right = knownIfAnyIsKnown(right, marker.right);
+ // Only continue if the other side is absent and this side changed.
+ return left.isFalse() && right != oldRight;
+ }
+ }
}
private static class InterfaceWithMarker {
@@ -287,12 +375,15 @@
return objectType;
}
- public static Set<DexType> computeLeastUpperBoundOfInterfaces(
- AppView<? extends AppInfoWithClassHierarchy> appView, Set<DexType> s1, Set<DexType> s2) {
+ public static InterfaceCollection computeLeastUpperBoundOfInterfaces(
+ AppView<? extends AppInfoWithClassHierarchy> appView,
+ InterfaceCollection s1,
+ InterfaceCollection s2) {
if (s1.isEmpty() || s2.isEmpty()) {
- return Collections.emptySet();
+ return InterfaceCollection.empty();
}
- Set<DexType> cached = appView.dexItemFactory().leastUpperBoundOfInterfacesTable.get(s1, s2);
+ InterfaceCollection cached =
+ appView.dexItemFactory().leastUpperBoundOfInterfacesTable.get(s1, s2);
if (cached != null) {
return cached;
}
@@ -300,58 +391,46 @@
if (cached != null) {
return cached;
}
- Map<DexType, Set<InterfaceMarker>> seen = new IdentityHashMap<>();
+ Map<DexType, InterfaceMarker> seen = new IdentityHashMap<>();
Queue<InterfaceWithMarker> worklist = new ArrayDeque<>();
- for (DexType itf1 : s1) {
- worklist.add(new InterfaceWithMarker(itf1, InterfaceMarker.LEFT));
- }
- for (DexType itf2 : s2) {
- worklist.add(new InterfaceWithMarker(itf2, InterfaceMarker.RIGHT));
- }
+ s1.forEach(
+ (itf1, isKnown) ->
+ worklist.add(new InterfaceWithMarker(itf1, InterfaceMarker.forLeft(isKnown))));
+ s2.forEach(
+ (itf2, isKnown) ->
+ worklist.add(new InterfaceWithMarker(itf2, InterfaceMarker.forRight(isKnown))));
+
while (!worklist.isEmpty()) {
InterfaceWithMarker item = worklist.poll();
DexType itf = item.itf;
InterfaceMarker marker = item.marker;
- Set<InterfaceMarker> markers = seen.computeIfAbsent(itf, k -> new HashSet<>());
- // If this interface is a lower one in this set, skip.
- if (markers.contains(marker)) {
- continue;
- }
- // If this interface is already visited by the other set, add marker for this set and skip.
- if (markers.size() == 1) {
- markers.add(marker);
- continue;
- }
- // Otherwise, this type is freshly visited.
- markers.add(marker);
- // Put super interfaces into the worklist.
- DexClass itfClass = appView.definitionFor(itf);
- if (itfClass != null) {
- for (DexType superItf : itfClass.interfaces.values) {
- markers = seen.computeIfAbsent(superItf, k -> new HashSet<>());
- if (!markers.contains(marker)) {
+ InterfaceMarker marking = seen.computeIfAbsent(itf, k -> InterfaceMarker.createUnmarked());
+ if (marking.merge(marker)) {
+ // Put super interfaces into the worklist.
+ DexClass itfClass = appView.definitionFor(itf);
+ if (itfClass != null) {
+ for (DexType superItf : itfClass.interfaces.values) {
worklist.add(new InterfaceWithMarker(superItf, marker));
}
}
}
}
- ImmutableSet.Builder<DexType> commonBuilder = ImmutableSet.builder();
- for (Map.Entry<DexType, Set<InterfaceMarker>> entry : seen.entrySet()) {
- // Keep commonly visited interfaces only
- if (entry.getValue().size() < 2) {
- continue;
- }
- commonBuilder.add(entry.getKey());
- }
- Set<DexType> commonlyVisited = commonBuilder.build();
+ List<Pair<DexType, Boolean>> commonlyVisited = new ArrayList<>(seen.size());
+ seen.forEach(
+ (itf, marking) -> {
+ // Keep commonly visited interfaces only
+ if (marking.isMarkedOnBothSides()) {
+ commonlyVisited.add(new Pair<>(itf, marking.knownIfBothAreKnown()));
+ }
+ });
- ImmutableSet.Builder<DexType> lubBuilder = ImmutableSet.builder();
- for (DexType itf : commonlyVisited) {
+ Builder lubBuilder = InterfaceCollection.builder();
+ for (Pair<DexType, Boolean> entry : commonlyVisited) {
// If there is a strict sub interface of this interface, it is not the least element.
boolean notTheLeast = false;
- for (DexType other : commonlyVisited) {
- if (appView.appInfo().isStrictSubtypeOf(other, itf)) {
+ for (Pair<DexType, Boolean> other : commonlyVisited) {
+ if (appView.appInfo().isStrictSubtypeOf(other.getFirst(), entry.getFirst())) {
notTheLeast = true;
break;
}
@@ -359,11 +438,11 @@
if (notTheLeast) {
continue;
}
- lubBuilder.add(itf);
+ lubBuilder.addInterface(entry.getFirst(), entry.getSecond());
}
- Set<DexType> lub = lubBuilder.build();
+ InterfaceCollection lub = lubBuilder.build();
// Cache the computation result only if the given two sets of interfaces are different.
- if (s1.size() != s2.size() || !s1.containsAll(s2)) {
+ if (!s1.equals(s2)) {
synchronized (appView.dexItemFactory().leastUpperBoundOfInterfacesTable) {
appView.dexItemFactory().leastUpperBoundOfInterfacesTable.put(s1, s2, lub);
}
@@ -386,14 +465,6 @@
if (!type.equals(other.type)) {
return false;
}
- Set<DexType> thisInterfaces = getInterfaces();
- Set<DexType> otherInterfaces = other.getInterfaces();
- if (thisInterfaces == otherInterfaces) {
- return true;
- }
- if (thisInterfaces.size() != otherInterfaces.size()) {
- return false;
- }
- return thisInterfaces.containsAll(otherInterfaces);
+ return getInterfaces().equals(other.getInterfaces());
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/type/InterfaceCollection.java b/src/main/java/com/android/tools/r8/ir/analysis/type/InterfaceCollection.java
new file mode 100644
index 0000000..7b80c82
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/analysis/type/InterfaceCollection.java
@@ -0,0 +1,163 @@
+// 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.analysis.type;
+
+import com.android.tools.r8.graph.DexClass;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.utils.InternalOptions;
+import com.android.tools.r8.utils.OptionalBool;
+import com.android.tools.r8.utils.Pair;
+import it.unimi.dsi.fastutil.objects.Reference2BooleanMap;
+import it.unimi.dsi.fastutil.objects.Reference2BooleanMap.Entry;
+import it.unimi.dsi.fastutil.objects.Reference2BooleanMaps;
+import it.unimi.dsi.fastutil.objects.Reference2BooleanOpenHashMap;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.function.BiConsumer;
+import java.util.function.BiPredicate;
+
+public class InterfaceCollection {
+
+ public static boolean isKnownToImplement(
+ DexType iface, DexType implementor, InternalOptions options) {
+ if (options.canHaveZipFileWithMissingCloseableBug()
+ && implementor == options.dexItemFactory().zipFileType
+ && iface == options.dexItemFactory().closeableType) {
+ return false;
+ }
+ return true;
+ }
+
+ public static class Builder {
+ private Reference2BooleanMap<DexType> interfaces = new Reference2BooleanOpenHashMap<>();
+
+ private Builder() {}
+
+ public Builder addInterface(DexType iface, DexClass implementor, InternalOptions options) {
+ return addInterface(
+ iface,
+ !implementor.isLibraryClass()
+ || isKnownToImplement(iface, implementor.getType(), options));
+ }
+
+ public Builder addInterface(DexType iface, DexType implementor, InternalOptions options) {
+ return addInterface(iface, isKnownToImplement(iface, implementor, options));
+ }
+
+ public Builder addInterface(DexType type, boolean isKnown) {
+ interfaces.compute(
+ type,
+ (existingType, existingIsKnown) ->
+ // If the entry is new 'existingIsKnown == null', so we join with (null or true).
+ (existingIsKnown == null || existingIsKnown) && isKnown);
+ return this;
+ }
+
+ public InterfaceCollection build() {
+ if (interfaces.isEmpty()) {
+ return InterfaceCollection.empty();
+ }
+ return new InterfaceCollection(interfaces);
+ }
+ }
+
+ private static final InterfaceCollection EMPTY =
+ new InterfaceCollection(Reference2BooleanMaps.emptyMap());
+
+ public static InterfaceCollection empty() {
+ return EMPTY;
+ }
+
+ public static InterfaceCollection singleton(DexType type) {
+ return new InterfaceCollection(Reference2BooleanMaps.singleton(type, true));
+ }
+
+ public static Builder builder() {
+ return new Builder();
+ }
+
+ /**
+ * Set of interfaces mapping to an optional presence.
+ *
+ * <ul>
+ * <li>An unmapped type is known to not be present.
+ * <li>A type mapped to true is known to always be present.
+ * <li>A type mapped to false is not always known to be present.
+ */
+ private final Reference2BooleanMap<DexType> interfaces;
+
+ private InterfaceCollection(Reference2BooleanMap<DexType> interfaces) {
+ assert interfaces != null;
+ this.interfaces = interfaces;
+ }
+
+ public boolean isEmpty() {
+ return interfaces.isEmpty();
+ }
+
+ public int size() {
+ return interfaces.size();
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (!(o instanceof InterfaceCollection)) {
+ return false;
+ }
+ InterfaceCollection that = (InterfaceCollection) o;
+ return interfaces.equals(that.interfaces);
+ }
+
+ @Override
+ public int hashCode() {
+ return interfaces.hashCode();
+ }
+
+ public void forEach(BiConsumer<DexType, Boolean> fn) {
+ interfaces.forEach(fn::accept);
+ }
+
+ public boolean anyMatch(BiPredicate<DexType, Boolean> fn) {
+ for (Entry<DexType> entry : interfaces.reference2BooleanEntrySet()) {
+ if (fn.test(entry.getKey(), entry.getBooleanValue())) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public List<Pair<DexType, Boolean>> getInterfaceList() {
+ List<Pair<DexType, Boolean>> list = new ArrayList<>(interfaces.size());
+ interfaces.forEach((iface, isKnown) -> list.add(new Pair<>(iface, isKnown)));
+ return list;
+ }
+
+ public boolean hasSingleKnownInterface() {
+ DexType singleKnownInterface = getSingleKnownInterface();
+ return singleKnownInterface != null;
+ }
+
+ public DexType getSingleKnownInterface() {
+ if (interfaces.size() != 1) {
+ return null;
+ }
+ DexType type = interfaces.keySet().iterator().next();
+ return interfaces.getBoolean(type) ? type : null;
+ }
+
+ public OptionalBool contains(DexType type) {
+ Boolean value = interfaces.get(type);
+ if (value == null) {
+ return OptionalBool.FALSE;
+ }
+ return value ? OptionalBool.TRUE : OptionalBool.unknown();
+ }
+
+ public boolean containsKnownInterface(DexType type) {
+ return contains(type).isTrue();
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/type/TypeAnalysis.java b/src/main/java/com/android/tools/r8/ir/analysis/type/TypeAnalysis.java
index 94b9489..d06fd44 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/type/TypeAnalysis.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/type/TypeAnalysis.java
@@ -18,7 +18,6 @@
import java.util.Comparator;
import java.util.Deque;
import java.util.List;
-import java.util.Set;
public class TypeAnalysis {
@@ -181,9 +180,9 @@
ClassTypeElement classType = receiverUpperBoundType.asClassType();
DexType refinedType = classType.getClassType();
if (refinedType == appView.dexItemFactory().objectType) {
- Set<DexType> interfaces = classType.getInterfaces();
- if (interfaces.size() == 1) {
- refinedType = interfaces.iterator().next();
+ DexType singleKnownInterface = classType.getInterfaces().getSingleKnownInterface();
+ if (singleKnownInterface != null) {
+ refinedType = singleKnownInterface;
}
}
if (appView.appInfo().isSubtype(refinedType, staticReceiverType)) {
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/type/TypeElement.java b/src/main/java/com/android/tools/r8/ir/analysis/type/TypeElement.java
index a2c8d57..9736b13 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/type/TypeElement.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/type/TypeElement.java
@@ -147,11 +147,7 @@
* @return {@code true} if {@param this} is strictly less than {@param other}.
*/
public boolean strictlyLessThan(TypeElement other, AppView<?> appView) {
- if (equals(other)) {
- return false;
- }
- TypeElement lub = join(other, appView);
- return !equals(lub) && other.equals(lub);
+ return !equals(other) && internalLessThan(other, appView);
}
/**
@@ -163,7 +159,13 @@
* @return {@code true} if {@param this} is less than or equal to {@param other}.
*/
public boolean lessThanOrEqual(TypeElement other, AppView<?> appView) {
- return equals(other) || strictlyLessThan(other, appView);
+ return equals(other) || internalLessThan(other, appView);
+ }
+
+ private boolean internalLessThan(TypeElement other, AppView<?> appView) {
+ // The equals check has already been done by callers, so only the join is computed.
+ TypeElement lub = join(other, appView);
+ return !equals(lub) && other.equals(lub);
}
/**
diff --git a/src/main/java/com/android/tools/r8/ir/code/BasicBlockInstructionListIterator.java b/src/main/java/com/android/tools/r8/ir/code/BasicBlockInstructionListIterator.java
index a1c4e7b..d0e0027 100644
--- a/src/main/java/com/android/tools/r8/ir/code/BasicBlockInstructionListIterator.java
+++ b/src/main/java/com/android/tools/r8/ir/code/BasicBlockInstructionListIterator.java
@@ -227,9 +227,9 @@
for (Value value : current.inValues()) {
value.removeUser(current);
}
- if (current.outValue() != null && current.outValue().isUsed()) {
+ if (current.hasUsedOutValue()) {
assert newInstruction.outValue() != null;
- if (affectedValues != null) {
+ if (affectedValues != null && newInstruction.getOutType() != current.getOutType()) {
current.outValue().addAffectedValuesTo(affectedValues);
}
current.outValue().replaceUsers(newInstruction.outValue());
@@ -243,6 +243,7 @@
listIterator.add(newInstruction);
current.clearBlock();
metadata.record(newInstruction);
+ current = newInstruction;
}
@Override
@@ -402,6 +403,69 @@
}
@Override
+ public void replaceCurrentInstructionWithThrow(
+ AppView<?> appView,
+ IRCode code,
+ ListIterator<BasicBlock> blockIterator,
+ Value exceptionValue,
+ Set<BasicBlock> blocksToRemove,
+ Set<Value> affectedValues) {
+ if (current == null) {
+ throw new IllegalStateException();
+ }
+
+ Instruction toBeReplaced = current;
+ InternalOptions options = appView.options();
+
+ BasicBlock block = toBeReplaced.getBlock();
+ assert !blocksToRemove.contains(block);
+ assert affectedValues != null;
+
+ // Split the block before the instruction that should be replaced by `throw exceptionValue`.
+ previous();
+
+ BasicBlock throwBlock;
+ if (block.hasCatchHandlers() && !toBeReplaced.instructionTypeCanThrow()) {
+ // We need to insert the throw instruction in a block of its own, so split the current block
+ // into three blocks, where the intermediate block only contains a goto instruction.
+ throwBlock = splitCopyCatchHandlers(code, blockIterator, options);
+ throwBlock.listIterator(code).split(code, blockIterator, true);
+ } else {
+ splitCopyCatchHandlers(code, blockIterator, options);
+ throwBlock = block;
+ }
+
+ // Unlink all blocks that are dominated by the unique normal successor of the throw block.
+ blocksToRemove.addAll(
+ throwBlock.unlink(
+ throwBlock.getUniqueNormalSuccessor(),
+ new DominatorTree(code, MAY_HAVE_UNREACHABLE_BLOCKS),
+ affectedValues));
+
+ InstructionListIterator throwBlockInstructionIterator;
+ if (throwBlock == block) {
+ throwBlockInstructionIterator = this;
+ previous();
+ next();
+ } else {
+ throwBlockInstructionIterator = throwBlock.listIterator(code, 1);
+ }
+ assert !throwBlockInstructionIterator.hasNext();
+
+ // Replace the instruction by throw.
+ Throw throwInstruction = new Throw(exceptionValue);
+ if (hasInsertionPosition()) {
+ throwInstruction.setPosition(position);
+ } else if (toBeReplaced.getPosition().isSome()) {
+ throwInstruction.setPosition(toBeReplaced.getPosition());
+ } else {
+ assert !toBeReplaced.instructionTypeCanThrow();
+ throwInstruction.setPosition(Position.syntheticNone());
+ }
+ throwBlockInstructionIterator.replaceCurrentInstruction(throwInstruction);
+ }
+
+ @Override
public void replaceCurrentInstructionWithThrowNull(
AppView<? extends AppInfoWithClassHierarchy> appView,
IRCode code,
@@ -563,8 +627,9 @@
IRCode code, ListIterator<BasicBlock> blockIterator, InternalOptions options) {
BasicBlock splitBlock = split(code, blockIterator, false);
assert !block.hasCatchHandlers();
- assert splitBlock.hasCatchHandlers();
- block.copyCatchHandlers(code, blockIterator, splitBlock, options);
+ if (splitBlock.hasCatchHandlers()) {
+ block.copyCatchHandlers(code, blockIterator, splitBlock, options);
+ }
return splitBlock;
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/FieldInstruction.java b/src/main/java/com/android/tools/r8/ir/code/FieldInstruction.java
index 7ec3198..fa42fcd 100644
--- a/src/main/java/com/android/tools/r8/ir/code/FieldInstruction.java
+++ b/src/main/java/com/android/tools/r8/ir/code/FieldInstruction.java
@@ -64,8 +64,18 @@
public boolean instructionInstanceCanThrow(
AppView<?> appView, ProgramMethod context, SideEffectAssumption assumption) {
- SuccessfulFieldResolutionResult resolutionResult =
- appView.appInfo().resolveField(field, context).asSuccessfulResolution();
+ return internalInstructionInstanceCanThrow(
+ appView,
+ context,
+ assumption,
+ appView.appInfo().resolveField(field, context).asSuccessfulResolution());
+ }
+
+ boolean internalInstructionInstanceCanThrow(
+ AppView<?> appView,
+ ProgramMethod context,
+ SideEffectAssumption assumption,
+ SuccessfulFieldResolutionResult resolutionResult) {
if (resolutionResult == null) {
return true;
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/IRCode.java b/src/main/java/com/android/tools/r8/ir/code/IRCode.java
index 01cf2ba..efc52d1 100644
--- a/src/main/java/com/android/tools/r8/ir/code/IRCode.java
+++ b/src/main/java/com/android/tools/r8/ir/code/IRCode.java
@@ -596,9 +596,13 @@
if (instruction.outValue != null && instruction.outValue.getType().isClassType()) {
ClassTypeElement classTypeLattice = instruction.outValue.getType().asClassType();
assert !verticallyMergedClasses.hasBeenMergedIntoSubtype(classTypeLattice.getClassType());
- for (DexType itf : classTypeLattice.getInterfaces()) {
- assert !verticallyMergedClasses.hasBeenMergedIntoSubtype(itf);
- }
+ assert !classTypeLattice
+ .getInterfaces()
+ .anyMatch(
+ (itf, isKnown) -> {
+ assert !verticallyMergedClasses.hasBeenMergedIntoSubtype(itf);
+ return false;
+ });
}
}
return true;
diff --git a/src/main/java/com/android/tools/r8/ir/code/IRCodeInstructionListIterator.java b/src/main/java/com/android/tools/r8/ir/code/IRCodeInstructionListIterator.java
index 757a581..4e223924 100644
--- a/src/main/java/com/android/tools/r8/ir/code/IRCodeInstructionListIterator.java
+++ b/src/main/java/com/android/tools/r8/ir/code/IRCodeInstructionListIterator.java
@@ -85,6 +85,17 @@
}
@Override
+ public void replaceCurrentInstructionWithThrow(
+ AppView<?> appView,
+ IRCode code,
+ ListIterator<BasicBlock> blockIterator,
+ Value exceptionValue,
+ Set<BasicBlock> blocksToRemove,
+ Set<Value> affectedValues) {
+ throw new Unimplemented();
+ }
+
+ @Override
public void replaceCurrentInstructionWithThrowNull(
AppView<? extends AppInfoWithClassHierarchy> appView,
IRCode code,
diff --git a/src/main/java/com/android/tools/r8/ir/code/InstancePut.java b/src/main/java/com/android/tools/r8/ir/code/InstancePut.java
index c38c5c0..20377f3 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InstancePut.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InstancePut.java
@@ -19,6 +19,7 @@
import com.android.tools.r8.graph.DexEncodedField;
import com.android.tools.r8.graph.DexField;
import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.FieldResolutionResult.SuccessfulFieldResolutionResult;
import com.android.tools.r8.graph.ProgramMethod;
import com.android.tools.r8.ir.analysis.ClassInitializationAnalysis;
import com.android.tools.r8.ir.analysis.ClassInitializationAnalysis.AnalysisAssumption;
@@ -121,12 +122,13 @@
AppView<AppInfoWithLiveness> appViewWithLiveness = appView.withLiveness();
AppInfoWithLiveness appInfoWithLiveness = appViewWithLiveness.appInfo();
- if (instructionInstanceCanThrow(appView, context, assumption)) {
+ SuccessfulFieldResolutionResult resolutionResult =
+ appInfoWithLiveness.resolveField(getField()).asSuccessfulResolution();
+ if (internalInstructionInstanceCanThrow(appView, context, assumption, resolutionResult)) {
return true;
}
- DexEncodedField encodedField =
- appInfoWithLiveness.resolveField(getField()).getResolvedField();
+ DexEncodedField encodedField = resolutionResult.getResolvedField();
assert encodedField != null : "NoSuchFieldError (resolution failure) should be caught.";
if (encodedField.type().isAlwaysNull(appViewWithLiveness)) {
diff --git a/src/main/java/com/android/tools/r8/ir/code/InstructionListIterator.java b/src/main/java/com/android/tools/r8/ir/code/InstructionListIterator.java
index 6b17a04..32b1f29 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InstructionListIterator.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InstructionListIterator.java
@@ -120,6 +120,14 @@
void replaceCurrentInstructionWithStaticGet(
AppView<?> appView, IRCode code, DexField field, Set<Value> affectedValues);
+ void replaceCurrentInstructionWithThrow(
+ AppView<?> appView,
+ IRCode code,
+ ListIterator<BasicBlock> blockIterator,
+ Value exceptionValue,
+ Set<BasicBlock> blocksToRemove,
+ Set<Value> affectedValues);
+
/**
* Replace the current instruction with null throwing instructions.
*
diff --git a/src/main/java/com/android/tools/r8/ir/code/InvokeCustom.java b/src/main/java/com/android/tools/r8/ir/code/InvokeCustom.java
index 29b8a3b..1ef9cf5 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InvokeCustom.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InvokeCustom.java
@@ -12,6 +12,8 @@
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.ProgramMethod;
import com.android.tools.r8.ir.analysis.type.ClassTypeElement;
+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.analysis.type.Nullability;
import com.android.tools.r8.ir.analysis.type.TypeElement;
import com.android.tools.r8.ir.conversion.CfBuilder;
@@ -19,9 +21,7 @@
import com.android.tools.r8.ir.desugar.LambdaDescriptor;
import com.android.tools.r8.ir.optimize.Inliner.ConstraintWithTarget;
import com.android.tools.r8.ir.optimize.InliningConstraints;
-import com.google.common.collect.ImmutableSet;
import java.util.List;
-import java.util.Set;
public final class InvokeCustom extends Invoke {
@@ -44,19 +44,18 @@
}
private static boolean verifyLambdaInterfaces(
- TypeElement returnType, Set<DexType> lambdaInterfaceSet, DexType objectType) {
- Set<DexType> primaryInterfaces = returnType.asClassType().getInterfaces();
+ TypeElement returnType, InterfaceCollection lambdaInterfaceSet, DexType objectType) {
+ InterfaceCollection primaryInterfaces = returnType.asClassType().getInterfaces();
if (returnType.asClassType().getClassType() == objectType) {
- assert primaryInterfaces.size() == 1;
// The interfaces returned by the LambdaDescriptor assumed to already contain the primary
// interface. If they're both singleton lists they must be identical and we can return the
// primary return type.
- assert lambdaInterfaceSet.contains(primaryInterfaces.iterator().next());
+ assert lambdaInterfaceSet.containsKnownInterface(primaryInterfaces.getSingleKnownInterface());
} else {
// We arrive here if the primary interface is a missing class. In that case the
// returnType will be the missing type as the class type.
assert primaryInterfaces.isEmpty();
- assert lambdaInterfaceSet.contains(returnType.asClassType().getClassType());
+ assert lambdaInterfaceSet.containsKnownInterface(returnType.asClassType().getClassType());
}
return true;
}
@@ -76,20 +75,21 @@
// The primary return type is either an interface or a missing type.
assert returnType instanceof ClassTypeElement;
- Set<DexType> primaryInterfaces = returnType.asClassType().getInterfaces();
+ InterfaceCollection primaryInterfaces = returnType.asClassType().getInterfaces();
DexType objectType = appView.dexItemFactory().objectType;
if (returnType.asClassType().getClassType() == objectType) {
- assert primaryInterfaces.size() == 1;
+ assert primaryInterfaces.hasSingleKnownInterface();
// Shortcut for the common case: single interface. Save creating a new lattice type.
if (lambdaInterfaces.size() == 1) {
- assert lambdaInterfaces.get(0) == primaryInterfaces.iterator().next();
+ assert lambdaInterfaces.get(0) == primaryInterfaces.getSingleKnownInterface();
return returnType;
}
}
- Set<DexType> lambdaInterfaceSet =
- ImmutableSet.<DexType>builder().addAll(lambdaInterfaces).build();
+ Builder builder = InterfaceCollection.builder();
+ lambdaInterfaces.forEach(iface -> builder.addInterface(iface, true));
+ InterfaceCollection lambdaInterfaceSet = builder.build();
assert verifyLambdaInterfaces(returnType, lambdaInterfaceSet, objectType);
diff --git a/src/main/java/com/android/tools/r8/ir/code/InvokeMethod.java b/src/main/java/com/android/tools/r8/ir/code/InvokeMethod.java
index 9d7d69b..0d9d21a 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InvokeMethod.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InvokeMethod.java
@@ -4,6 +4,7 @@
package com.android.tools.r8.ir.code;
import static com.android.tools.r8.graph.DexProgramClass.asProgramClassOrNull;
+import static com.android.tools.r8.ir.analysis.type.Nullability.maybeNull;
import com.android.tools.r8.cf.LoadStoreHelper;
import com.android.tools.r8.cf.TypeVerificationHelper;
@@ -21,6 +22,7 @@
import com.android.tools.r8.ir.analysis.modeling.LibraryMethodReadSetModeling;
import com.android.tools.r8.ir.analysis.type.ClassTypeElement;
import com.android.tools.r8.ir.analysis.type.TypeAnalysis;
+import com.android.tools.r8.ir.analysis.type.TypeElement;
import com.android.tools.r8.ir.analysis.value.AbstractValue;
import com.android.tools.r8.ir.analysis.value.UnknownValue;
import com.android.tools.r8.ir.optimize.DefaultInliningOracle;
@@ -253,6 +255,11 @@
return self();
}
+ public B setFreshOutValue(AppView<?> appView, ValueFactory factory) {
+ return super.setFreshOutValue(
+ factory, TypeElement.fromDexType(method.getReturnType(), maybeNull(), appView));
+ }
+
public B setSingleArgument(Value argument) {
return setArguments(ImmutableList.of(argument));
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/LinearFlowInstructionListIterator.java b/src/main/java/com/android/tools/r8/ir/code/LinearFlowInstructionListIterator.java
index bbb6d27..3e61866 100644
--- a/src/main/java/com/android/tools/r8/ir/code/LinearFlowInstructionListIterator.java
+++ b/src/main/java/com/android/tools/r8/ir/code/LinearFlowInstructionListIterator.java
@@ -100,6 +100,18 @@
}
@Override
+ public void replaceCurrentInstructionWithThrow(
+ AppView<?> appView,
+ IRCode code,
+ ListIterator<BasicBlock> blockIterator,
+ Value exceptionValue,
+ Set<BasicBlock> blocksToRemove,
+ Set<Value> affectedValues) {
+ currentBlockIterator.replaceCurrentInstructionWithThrow(
+ appView, code, blockIterator, exceptionValue, blocksToRemove, affectedValues);
+ }
+
+ @Override
public void replaceCurrentInstructionWithThrowNull(
AppView<? extends AppInfoWithClassHierarchy> appView,
IRCode code,
diff --git a/src/main/java/com/android/tools/r8/ir/code/StaticPut.java b/src/main/java/com/android/tools/r8/ir/code/StaticPut.java
index 82d67b0..1017f53 100644
--- a/src/main/java/com/android/tools/r8/ir/code/StaticPut.java
+++ b/src/main/java/com/android/tools/r8/ir/code/StaticPut.java
@@ -18,6 +18,7 @@
import com.android.tools.r8.graph.DexEncodedField;
import com.android.tools.r8.graph.DexField;
import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.FieldResolutionResult.SuccessfulFieldResolutionResult;
import com.android.tools.r8.graph.ProgramMethod;
import com.android.tools.r8.ir.analysis.ClassInitializationAnalysis;
import com.android.tools.r8.ir.analysis.ClassInitializationAnalysis.AnalysisAssumption;
@@ -28,7 +29,6 @@
import com.android.tools.r8.ir.optimize.InliningConstraints;
import com.android.tools.r8.ir.regalloc.RegisterAllocator;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
-import com.android.tools.r8.shaking.ProguardMemberRule;
public class StaticPut extends FieldInstruction implements StaticFieldInstruction {
@@ -99,22 +99,14 @@
if (appView.appInfo().hasLiveness()) {
AppView<AppInfoWithLiveness> appViewWithLiveness = appView.withLiveness();
AppInfoWithLiveness appInfoWithLiveness = appViewWithLiveness.appInfo();
- // MemberValuePropagation will replace the field read only if the target field has bound
- // -assumevalues rule whose return value is *single*.
- //
- // Note that, in principle, class initializer of the field's holder may have side effects.
- // However, with -assumevalues, we assume that the developer wants to remove field accesses.
- ProguardMemberRule rule = appInfoWithLiveness.assumedValues.get(getField());
- if (rule != null && rule.getReturnValue().isSingleValue()) {
- return false;
- }
- if (instructionInstanceCanThrow(appView, context, assumption)) {
+ SuccessfulFieldResolutionResult resolutionResult =
+ appInfoWithLiveness.resolveField(getField()).asSuccessfulResolution();
+ if (internalInstructionInstanceCanThrow(appView, context, assumption, resolutionResult)) {
return true;
}
- DexEncodedField encodedField =
- appInfoWithLiveness.resolveField(getField()).getResolvedField();
+ DexEncodedField encodedField = resolutionResult.getResolvedField();
assert encodedField != null : "NoSuchFieldError (resolution failure) should be caught.";
boolean isDeadProtoExtensionField =
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/CfSourceCode.java b/src/main/java/com/android/tools/r8/ir/conversion/CfSourceCode.java
index de727c4..5df390f 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/CfSourceCode.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/CfSourceCode.java
@@ -407,7 +407,7 @@
if (!getMethod().isStatic()) {
firstLocalIndex++;
}
- for (DexType value : getMethod().proto().parameters.values) {
+ for (DexType value : getMethod().getProto().parameters.values) {
firstLocalIndex++;
if (value.isLongType() || value.isDoubleType()) {
firstLocalIndex++;
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/ClassConverter.java b/src/main/java/com/android/tools/r8/ir/conversion/ClassConverter.java
new file mode 100644
index 0000000..e747891
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/conversion/ClassConverter.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.ir.conversion;
+
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexApplication;
+import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.utils.ThreadUtils;
+import com.google.common.collect.Sets;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorService;
+
+public abstract class ClassConverter {
+
+ private final IRConverter converter;
+ private final D8MethodProcessor methodProcessor;
+
+ ClassConverter(IRConverter converter, D8MethodProcessor methodProcessor) {
+ this.converter = converter;
+ this.methodProcessor = methodProcessor;
+ }
+
+ public static ClassConverter create(
+ AppView<?> appView, IRConverter converter, D8MethodProcessor methodProcessor) {
+ return appView.options().desugarSpecificOptions().allowAllDesugaredInput
+ ? new LibraryDesugaredClassConverter(appView, converter, methodProcessor)
+ : new DefaultClassConverter(converter, methodProcessor);
+ }
+
+ public void convertClasses(DexApplication application, ExecutorService executorService)
+ throws ExecutionException {
+ internalConvertClasses(application, executorService);
+ notifyAllClassesConverted();
+ }
+
+ private void internalConvertClasses(DexApplication application, ExecutorService executorService)
+ throws ExecutionException {
+ List<DexProgramClass> classes = application.classes();
+ while (!classes.isEmpty()) {
+ Set<DexType> seenNestHosts = Sets.newIdentityHashSet();
+ List<DexProgramClass> deferred = new ArrayList<>(classes.size() / 2);
+ List<DexProgramClass> wave = new ArrayList<>(classes.size());
+ for (DexProgramClass clazz : classes) {
+ if (clazz.isInANest() && !seenNestHosts.add(clazz.getNestHost())) {
+ deferred.add(clazz);
+ } else {
+ wave.add(clazz);
+ }
+ }
+ ThreadUtils.processItems(wave, this::convertClass, executorService);
+ classes = deferred;
+ }
+ methodProcessor.awaitMethodProcessing();
+ }
+
+ abstract void convertClass(DexProgramClass clazz);
+
+ void convertMethods(DexProgramClass clazz) {
+ converter.convertMethods(clazz, methodProcessor);
+ }
+
+ abstract void notifyAllClassesConverted();
+
+ static class DefaultClassConverter extends ClassConverter {
+
+ DefaultClassConverter(IRConverter converter, D8MethodProcessor methodProcessor) {
+ super(converter, methodProcessor);
+ }
+
+ @Override
+ void convertClass(DexProgramClass clazz) {
+ convertMethods(clazz);
+ }
+
+ @Override
+ void notifyAllClassesConverted() {
+ // Intentionally empty.
+ }
+ }
+
+ static class LibraryDesugaredClassConverter extends ClassConverter {
+
+ private final AppView<?> appView;
+ private final Set<DexType> alreadyLibraryDesugared = Sets.newConcurrentHashSet();
+
+ LibraryDesugaredClassConverter(
+ AppView<?> appView, IRConverter converter, D8MethodProcessor methodProcessor) {
+ super(converter, methodProcessor);
+ this.appView = appView;
+ }
+
+ @Override
+ void convertClass(DexProgramClass clazz) {
+ // Classes which has already been through library desugaring will not go through IR
+ // processing again.
+ LibraryDesugaredChecker libraryDesugaredChecker = new LibraryDesugaredChecker(appView);
+ if (libraryDesugaredChecker.isClassLibraryDesugared(clazz)) {
+ alreadyLibraryDesugared.add(clazz.getType());
+ } else {
+ convertMethods(clazz);
+ }
+ }
+
+ @Override
+ void notifyAllClassesConverted() {
+ appView.setAlreadyLibraryDesugared(alreadyLibraryDesugared);
+ }
+ }
+}
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
new file mode 100644
index 0000000..61ea47c
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/conversion/D8MethodProcessor.java
@@ -0,0 +1,50 @@
+// 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.ir.optimize.info.OptimizationFeedbackIgnore;
+import com.android.tools.r8.utils.ThreadUtils;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Future;
+
+public class D8MethodProcessor extends MethodProcessor {
+
+ private final IRConverter converter;
+ private final ExecutorService executorService;
+ private final List<Future<?>> futures = Collections.synchronizedList(new ArrayList<>());
+
+ public D8MethodProcessor(IRConverter converter, ExecutorService executorService) {
+ this.converter = converter;
+ this.executorService = executorService;
+ }
+
+ public void awaitMethodProcessing() throws ExecutionException {
+ ThreadUtils.awaitFutures(futures);
+ futures.clear();
+ }
+
+ @Override
+ public boolean shouldApplyCodeRewritings(ProgramMethod method) {
+ return true;
+ }
+
+ @Override
+ public void scheduleMethodForProcessingAfterCurrentWave(ProgramMethod method) {
+ futures.add(
+ ThreadUtils.processAsynchronously(
+ () ->
+ converter.rewriteCode(method, OptimizationFeedbackIgnore.getInstance(), this, null),
+ executorService));
+ }
+
+ public boolean verifyAllMethodsProcessed() {
+ assert futures.isEmpty();
+ return true;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/IRBuilder.java b/src/main/java/com/android/tools/r8/ir/conversion/IRBuilder.java
index 27a8c72..1bee2d4 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/IRBuilder.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/IRBuilder.java
@@ -2402,7 +2402,7 @@
}
// Move all normal successors to the new block.
- currentBlockInfo.normalSuccessors.forEach((Integer offset) -> info.addNormalSuccessor(offset));
+ currentBlockInfo.normalSuccessors.forEach(info::addNormalSuccessor);
currentBlockInfo.normalSuccessors.clear();
// Link the two blocks.
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 d08eb3e..dd9bee9 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
@@ -45,7 +45,6 @@
import com.android.tools.r8.ir.code.Value;
import com.android.tools.r8.ir.desugar.BackportedMethodRewriter;
import com.android.tools.r8.ir.desugar.CovariantReturnTypeAnnotationTransformer;
-import com.android.tools.r8.ir.desugar.D8NestBasedAccessDesugaring;
import com.android.tools.r8.ir.desugar.DesugaredLibraryAPIConverter;
import com.android.tools.r8.ir.desugar.DesugaredLibraryAPIConverter.Mode;
import com.android.tools.r8.ir.desugar.DesugaredLibraryRetargeter;
@@ -54,6 +53,8 @@
import com.android.tools.r8.ir.desugar.LambdaRewriter;
import com.android.tools.r8.ir.desugar.StringConcatRewriter;
import com.android.tools.r8.ir.desugar.TwrCloseResourceRewriter;
+import com.android.tools.r8.ir.desugar.nest.D8NestBasedAccessDesugaring;
+import com.android.tools.r8.ir.desugar.nest.NestBridgeConsumer;
import com.android.tools.r8.ir.optimize.AssertionsRewriter;
import com.android.tools.r8.ir.optimize.AssumeInserter;
import com.android.tools.r8.ir.optimize.ClassInitializerDefaultsOptimization;
@@ -108,12 +109,10 @@
import com.android.tools.r8.utils.collections.SortedProgramMethodSet;
import com.google.common.base.Suppliers;
import com.google.common.collect.ImmutableList;
-import com.google.common.collect.Sets;
import java.util.ArrayList;
import java.util.Collection;
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.concurrent.atomic.AtomicBoolean;
@@ -240,7 +239,9 @@
new DesugaredLibraryAPIConverter(appView, Mode.GENERATE_CALLBACKS_AND_WRAPPERS);
this.backportedMethodRewriter = new BackportedMethodRewriter(appView);
this.twrCloseResourceRewriter =
- enableTwrCloseResourceDesugaring() ? new TwrCloseResourceRewriter(appView, this) : null;
+ TwrCloseResourceRewriter.enableTwrCloseResourceDesugaring(appView.options())
+ ? new TwrCloseResourceRewriter(appView)
+ : null;
this.d8NestBasedAccessDesugaring =
options.shouldDesugarNests() ? new D8NestBasedAccessDesugaring(appView) : null;
this.lambdaMerger = null;
@@ -274,8 +275,9 @@
? new InterfaceMethodRewriter(appView, this)
: null;
this.twrCloseResourceRewriter =
- (options.desugarState == DesugarState.ON && enableTwrCloseResourceDesugaring())
- ? new TwrCloseResourceRewriter(appView, this)
+ (TwrCloseResourceRewriter.enableTwrCloseResourceDesugaring(options)
+ && !appView.enableWholeProgramOptimizations())
+ ? new TwrCloseResourceRewriter(appView)
: null;
this.backportedMethodRewriter =
(options.desugarState == DesugarState.ON && !appView.enableWholeProgramOptimizations())
@@ -322,7 +324,9 @@
this.identifierNameStringMarker = null;
}
this.devirtualizer =
- options.enableDevirtualization ? new Devirtualizer(appViewWithLiveness) : null;
+ options.enableDevirtualization
+ ? new Devirtualizer(appViewWithLiveness, mainDexClasses)
+ : null;
this.typeChecker = new TypeChecker(appViewWithLiveness, VerifyTypesHelper.create(appView));
this.d8NestBasedAccessDesugaring = null;
this.serviceLoaderRewriter =
@@ -382,38 +386,34 @@
this(AppView.createForD8(appInfo), timing, printer, MainDexTracingResult.NONE);
}
- private boolean enableTwrCloseResourceDesugaring() {
- return enableTryWithResourcesDesugaring() && !options.canUseTwrCloseResourceMethod();
- }
-
- private boolean enableTryWithResourcesDesugaring() {
- switch (options.tryWithResourcesDesugaring) {
- case Off:
- return false;
- case Auto:
- return !options.canUseSuppressedExceptions();
- }
- throw new Unreachable();
- }
-
private void removeLambdaDeserializationMethods() {
if (lambdaRewriter != null) {
lambdaRewriter.removeLambdaDeserializationMethods(appView.appInfo().classes());
}
}
- private void desugarNestBasedAccess(Builder<?> builder, ExecutorService executorService)
- throws ExecutionException {
+ private void synthesizeBridgesForNestBasedAccessesOnClasspath(
+ MethodProcessor methodProcessor, ExecutorService executorService) throws ExecutionException {
if (d8NestBasedAccessDesugaring != null) {
- d8NestBasedAccessDesugaring.desugarNestBasedAccess(builder, executorService, this);
+ d8NestBasedAccessDesugaring.synthesizeBridgesForNestBasedAccessesOnClasspath(
+ methodProcessor, executorService);
}
}
- private void synthesizeLambdaClasses(Builder<?> builder, ExecutorService executorService)
- throws ExecutionException {
+ private void finalizeNestBasedAccessDesugaring(Builder<?> builder) {
+ if (d8NestBasedAccessDesugaring != null) {
+ DexProgramClass nestConstructor = d8NestBasedAccessDesugaring.synthesizeNestConstructor();
+ if (nestConstructor != null) {
+ builder.addProgramClass(nestConstructor);
+ }
+ d8NestBasedAccessDesugaring.reportDesugarDependencies();
+ }
+ }
+
+ private void synthesizeLambdaClasses(ExecutorService executorService) throws ExecutionException {
if (lambdaRewriter != null) {
assert !appView.enableWholeProgramOptimizations();
- lambdaRewriter.finalizeLambdaDesugaringForD8(builder, this, executorService);
+ lambdaRewriter.finalizeLambdaDesugaringForD8(this, executorService);
}
}
@@ -435,16 +435,16 @@
Flavor includeAllResources,
ExecutorService executorService)
throws ExecutionException {
+ assert !appView.getSyntheticItems().hasPendingSyntheticClasses();
if (interfaceMethodRewriter != null) {
interfaceMethodRewriter.desugarInterfaceMethods(
builder, includeAllResources, executorService);
}
}
- private void synthesizeTwrCloseResourceUtilityClass(
- Builder<?> builder, ExecutorService executorService) throws ExecutionException {
+ private void processTwrCloseResourceUtilityMethods() {
if (twrCloseResourceRewriter != null) {
- twrCloseResourceRewriter.synthesizeUtilityClass(builder, executorService, options);
+ twrCloseResourceRewriter.processSynthesizedMethods(this);
}
}
@@ -489,41 +489,27 @@
DexApplication application = appView.appInfo().app();
timing.begin("IR conversion");
- if (appView.options().desugarSpecificOptions().allowAllDesugaredInput) {
- // Classes which has already been through library desugaring will not go through IR
- // processing again.
- LibraryDesugaredChecker libraryDesugaredChecker = new LibraryDesugaredChecker(appView);
- Set<DexType> alreadyLibraryDesugared = Sets.newConcurrentHashSet();
- ThreadUtils.processItems(
- application.classes(),
- clazz -> {
- if (libraryDesugaredChecker.isClassLibraryDesugared(clazz)) {
- if (appView.options().desugarSpecificOptions().allowAllDesugaredInput) {
- alreadyLibraryDesugared.add(clazz.getType());
- } else {
- throw new CompilationError(
- "Code for "
- + clazz.getType().getDescriptor()
- + "has already been library desugared.");
- }
- } else {
- convertMethods(clazz);
- }
- },
- executor);
- appView.setAlreadyLibraryDesugared(alreadyLibraryDesugared);
- } else {
- ThreadUtils.processItems(application.classes(), this::convertMethods, executor);
- }
+ convertClasses(application, executor);
// Build a new application with jumbo string info,
- Builder<?> builder = application.builder();
- builder.setHighestSortingString(highestSortingString);
+ Builder<?> builder = application.builder().setHighestSortingString(highestSortingString);
- desugarNestBasedAccess(builder, executor);
- synthesizeLambdaClasses(builder, executor);
+ finalizeNestBasedAccessDesugaring(builder);
+
+ // Synthesize lambda classes and commit to the app in full.
+ synthesizeLambdaClasses(executor);
+ processTwrCloseResourceUtilityMethods();
+ if (appView.getSyntheticItems().hasPendingSyntheticClasses()) {
+ appView.setAppInfo(
+ new AppInfo(
+ appView.appInfo().getSyntheticItems().commit(builder.build()),
+ appView.appInfo().getMainDexClasses()));
+ application = appView.appInfo().app();
+ builder = application.builder();
+ builder.setHighestSortingString(highestSortingString);
+ }
+
desugarInterfaceMethods(builder, ExcludeDexResources, executor);
- synthesizeTwrCloseResourceUtilityClass(builder, executor);
processSynthesizedJava8UtilityClasses(executor);
synthesizeRetargetClass(builder, executor);
synthesizeInvokeSpecialBridges(executor);
@@ -532,14 +518,24 @@
timing.end();
- DexApplication app = builder.build();
+ application = builder.build();
appView.setAppInfo(
new AppInfo(
- appView.appInfo().getSyntheticItems().commit(app),
+ appView.appInfo().getSyntheticItems().commit(application),
appView.appInfo().getMainDexClasses()));
}
- private void convertMethods(DexProgramClass clazz) {
+ private void convertClasses(DexApplication application, ExecutorService executorService)
+ throws ExecutionException {
+ D8MethodProcessor methodProcessor = new D8MethodProcessor(this, executorService);
+ ClassConverter classConverter = ClassConverter.create(appView, this, methodProcessor);
+ classConverter.convertClasses(application, executorService);
+
+ synthesizeBridgesForNestBasedAccessesOnClasspath(methodProcessor, executorService);
+ methodProcessor.awaitMethodProcessing();
+ }
+
+ void convertMethods(DexProgramClass clazz, MethodProcessor methodProcessor) {
boolean isReachabilitySensitive = clazz.hasReachabilitySensitiveAnnotation(options.itemFactory);
// When converting all methods on a class always convert <clinit> first.
DexEncodedMethod classInitializer = clazz.getClassInitializer();
@@ -547,14 +543,14 @@
classInitializer
.getMutableOptimizationInfo()
.setReachabilitySensitive(isReachabilitySensitive);
- convertMethod(new ProgramMethod(clazz, classInitializer));
+ convertMethod(new ProgramMethod(clazz, classInitializer), methodProcessor);
}
clazz.forEachProgramMethodMatching(
definition -> !definition.isClassInitializer(),
method -> {
DexEncodedMethod definition = method.getDefinition();
definition.getMutableOptimizationInfo().setReachabilitySensitive(isReachabilitySensitive);
- convertMethod(method);
+ convertMethod(method, methodProcessor);
});
// The class file version is downgraded after compilation. Some of the desugaring might need
// the initial class file version to determine how far a method can be downgraded.
@@ -564,7 +560,7 @@
}
}
- private void convertMethod(ProgramMethod method) {
+ private void convertMethod(ProgramMethod method, MethodProcessor methodProcessor) {
DexEncodedMethod definition = method.getDefinition();
if (definition.hasClassFileVersion()) {
definition.downgradeClassFileVersion(
@@ -583,8 +579,7 @@
if (options.isGeneratingClassFiles()
|| !(options.passthroughDexCode && definition.getCode().isDexCode())) {
// We do not process in call graph order, so anything could be a leaf.
- rewriteCode(
- method, simpleOptimizationFeedback, OneTimeMethodProcessor.create(method, appView), null);
+ rewriteCode(method, simpleOptimizationFeedback, methodProcessor, null);
} else {
assert definition.getCode().isDexCode();
}
@@ -760,6 +755,7 @@
} else {
appView.setUnboxedEnums(EnumDataMap.empty());
}
+
if (!options.debug) {
new TrivialFieldAccessReprocessor(appView.withLiveness(), postMethodProcessorBuilder)
.run(executorService, feedback, timing);
@@ -768,7 +764,7 @@
timing.begin("IR conversion phase 2");
graphLensForIR = appView.graphLens();
PostMethodProcessor postMethodProcessor =
- postMethodProcessorBuilder.build(appView.withLiveness(), executorService, timing);
+ postMethodProcessorBuilder.build(appView, executorService, timing);
if (postMethodProcessor != null) {
assert !options.debug;
postMethodProcessor.forEachWaveWithExtension(feedback, executorService);
@@ -793,6 +789,14 @@
appView.clearCodeRewritings();
}
+ // Commit synthetics before creating a builder (otherwise the builder will not include the
+ // synthetics.)
+ if (appView.getSyntheticItems().hasPendingSyntheticClasses()) {
+ appView.setAppInfo(
+ appView
+ .appInfo()
+ .rebuildWithLiveness(appView.getSyntheticItems().commit(appView.appInfo().app())));
+ }
// Build a new application with jumbo string info.
Builder<?> builder = appView.appInfo().app().builder();
builder.setHighestSortingString(highestSortingString);
@@ -802,7 +806,6 @@
feedback.updateVisibleOptimizationInfo();
printPhase("Utility classes synthesis");
- synthesizeTwrCloseResourceUtilityClass(builder, executorService);
processSynthesizedJava8UtilityClasses(executorService);
synthesizeRetargetClass(builder, executorService);
synthesizeEnumUnboxingUtilityMethods(executorService);
@@ -814,11 +817,9 @@
printPhase("Desugared library API Conversion finalization");
generateDesugaredLibraryAPIWrappers(builder, executorService);
- if (serviceLoaderRewriter != null && serviceLoaderRewriter.getSynthesizedClass() != null) {
- appView.appInfo().addSynthesizedClass(serviceLoaderRewriter.getSynthesizedClass(), true);
+ if (serviceLoaderRewriter != null) {
processSynthesizedServiceLoaderMethods(
- serviceLoaderRewriter.getSynthesizedClass(), executorService);
- builder.addSynthesizedClass(serviceLoaderRewriter.getSynthesizedClass());
+ serviceLoaderRewriter.getServiceLoadMethods(), executorService);
}
// Update optimization info for all synthesized methods at once.
@@ -959,11 +960,10 @@
}
private void processSynthesizedServiceLoaderMethods(
- DexProgramClass synthesizedClass, ExecutorService executorService) throws ExecutionException {
+ List<ProgramMethod> serviceLoadMethods, ExecutorService executorService)
+ throws ExecutionException {
ThreadUtils.processItems(
- synthesizedClass::forEachProgramMethod,
- this::forEachSynthesizedServiceLoaderMethod,
- executorService);
+ serviceLoadMethods, this::forEachSynthesizedServiceLoaderMethod, executorService);
}
private void forEachSynthesizedServiceLoaderMethod(ProgramMethod method) {
@@ -1135,7 +1135,7 @@
}
}
- private Timing rewriteCode(
+ Timing rewriteCode(
ProgramMethod method,
OptimizationFeedback feedback,
MethodProcessor methodProcessor,
@@ -1162,7 +1162,7 @@
method.toSourceString(),
logCode(options, method.getDefinition()));
}
- boolean didDesugar = desugar(method);
+ boolean didDesugar = desugar(method, methodProcessor);
if (Log.ENABLED && didDesugar) {
Log.debug(
getClass(),
@@ -1185,7 +1185,7 @@
return optimize(code, feedback, methodProcessor, methodProcessingId);
}
- private boolean desugar(ProgramMethod method) {
+ private boolean desugar(ProgramMethod method, MethodProcessor methodProcessor) {
if (options.desugarState != DesugarState.ON) {
return false;
}
@@ -1194,13 +1194,17 @@
}
boolean didDesugar = false;
Supplier<AppInfoWithClassHierarchy> lazyAppInfo =
- Suppliers.memoize(() -> appView.appInfoForDesugaring());
+ Suppliers.memoize(appView::appInfoForDesugaring);
if (lambdaRewriter != null) {
didDesugar |= lambdaRewriter.desugarLambdas(method, lazyAppInfo.get()) > 0;
}
if (backportedMethodRewriter != null) {
didDesugar |= backportedMethodRewriter.desugar(method, lazyAppInfo.get());
}
+ if (d8NestBasedAccessDesugaring != null) {
+ NestBridgeConsumer bridgeConsumer = NestBridgeConsumer.createForD8(methodProcessor);
+ didDesugar |= d8NestBasedAccessDesugaring.desugar(method, bridgeConsumer);
+ }
return didDesugar;
}
@@ -1247,10 +1251,15 @@
timing.end();
}
- assert !method.isProcessed() || !isDebugMode;
+ assert !method.isProcessed() || !isDebugMode
+ : "Method already processed: "
+ + context.toSourceString()
+ + System.lineSeparator()
+ + ExceptionUtils.getMainStackTrace();
assert !method.isProcessed()
- || !appView.enableWholeProgramOptimizations()
- || !appView.appInfo().withLiveness().isNeverReprocessMethod(method.method);
+ || !appView.enableWholeProgramOptimizations()
+ || !appView.appInfo().withLiveness().isNeverReprocessMethod(method.method)
+ : "Illegal reprocessing due to -neverreprocess rule: " + context.toSourceString();
if (lambdaMerger != null) {
timing.begin("Merge lambdas");
@@ -1474,7 +1483,7 @@
deadCodeRemover.run(code, timing);
assert code.isConsistentSSA();
- if (options.desugarState == DesugarState.ON && enableTryWithResourcesDesugaring()) {
+ if (options.desugarState == DesugarState.ON && options.enableTryWithResourcesDesugaring()) {
timing.begin("Rewrite Throwable suppresed methods");
codeRewriter.rewriteThrowableAddAndGetSuppressed(code);
timing.end();
@@ -1530,18 +1539,9 @@
previous = printMethod(code, "IR after class inlining (SSA)", previous);
- if (d8NestBasedAccessDesugaring != null) {
- timing.begin("Desugar nest access");
- d8NestBasedAccessDesugaring.rewriteNestBasedAccesses(context, code, appView);
- timing.end();
- assert code.isConsistentSSA();
- }
-
- previous = printMethod(code, "IR after nest based access desugaring (SSA)", previous);
-
if (interfaceMethodRewriter != null) {
timing.begin("Rewrite interface methods");
- interfaceMethodRewriter.rewriteMethodReferences(code);
+ interfaceMethodRewriter.rewriteMethodReferences(code, methodProcessor, methodProcessingId);
timing.end();
assert code.isConsistentSSA();
}
@@ -1550,7 +1550,8 @@
// This pass has to be after interfaceMethodRewriter and BackportedMethodRewriter.
if (desugaredLibraryAPIConverter != null
- && (!appView.enableWholeProgramOptimizations() || methodProcessor.isPrimary())) {
+ && (!appView.enableWholeProgramOptimizations()
+ || methodProcessor.isPrimaryMethodProcessor())) {
timing.begin("Desugar library API");
desugaredLibraryAPIConverter.desugar(code);
timing.end();
@@ -1561,7 +1562,7 @@
if (twrCloseResourceRewriter != null) {
timing.begin("Rewrite TWR close");
- twrCloseResourceRewriter.rewriteMethodCode(code);
+ twrCloseResourceRewriter.rewriteIR(code);
timing.end();
}
@@ -1580,7 +1581,7 @@
// 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.isPrimary()) {
+ if (options.outline.enabled && outliner != null && methodProcessor.isPrimaryMethodProcessor()) {
timing.begin("Identify outlines");
outliner.getOutlineMethodIdentifierGenerator().accept(code);
timing.end();
@@ -1633,7 +1634,7 @@
timing.end();
}
- if (enumUnboxer != null && methodProcessor.isPrimary()) {
+ if (enumUnboxer != null && methodProcessor.isPrimaryMethodProcessor()) {
enumUnboxer.analyzeEnums(code);
}
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 86ff63f..ef9a31c 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
@@ -8,25 +8,13 @@
public abstract class MethodProcessor {
- public enum Phase {
- ONE_TIME,
- PRIMARY,
- POST
- }
-
protected SortedProgramMethodSet wave;
protected SortedProgramMethodSet waveExtension = SortedProgramMethodSet.createConcurrent();
- public abstract Phase getPhase();
-
public abstract boolean shouldApplyCodeRewritings(ProgramMethod method);
- public boolean isPrimary() {
- return getPhase() == Phase.PRIMARY;
- }
-
- public boolean isPost() {
- return getPhase() == Phase.POST;
+ public boolean isPrimaryMethodProcessor() {
+ return false;
}
public CallSiteInformation getCallSiteInformation() {
@@ -41,7 +29,7 @@
waveExtension.add(method);
}
- protected final void prepareForWaveExtensionProcessing() {
+ protected void prepareForWaveExtensionProcessing() {
if (waveExtension.isEmpty()) {
wave = SortedProgramMethodSet.empty();
} else {
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/NeedsIRDesugarUseRegistry.java b/src/main/java/com/android/tools/r8/ir/conversion/NeedsIRDesugarUseRegistry.java
index f348214..c44ce41 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/NeedsIRDesugarUseRegistry.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/NeedsIRDesugarUseRegistry.java
@@ -18,7 +18,7 @@
class NeedsIRDesugarUseRegistry extends UseRegistry {
- private boolean needsDesugarging = false;
+ private boolean needsDesugaring = false;
private final AppView appView;
private final BackportedMethodRewriter backportedMethodRewriter;
private final DesugaredLibraryRetargeter desugaredLibraryRetargeter;
@@ -40,15 +40,15 @@
}
public boolean needsDesugaring() {
- return needsDesugarging;
+ return needsDesugaring;
}
@Override
public void registerInitClass(DexType type) {
- if (!needsDesugarging
+ if (!needsDesugaring
&& desugaredLibraryAPIConverter != null
&& desugaredLibraryAPIConverter.canConvert(type)) {
- needsDesugarging = true;
+ needsDesugaring = true;
}
}
@@ -67,31 +67,38 @@
registerDesugaredLibraryAPIConverter(method);
}
+ private void registerTwrCloseResourceRewriting(DexMethod method) {
+ if (!needsDesugaring) {
+ needsDesugaring =
+ TwrCloseResourceRewriter.isTwrCloseResourceMethod(method, appView.dexItemFactory());
+ }
+ }
+
private void registerBackportedMethodRewriting(DexMethod method) {
- if (!needsDesugarging) {
- needsDesugarging = backportedMethodRewriter.needsDesugaring(method);
+ if (!needsDesugaring) {
+ needsDesugaring = backportedMethodRewriter.needsDesugaring(method);
}
}
private void registerInterfaceMethodRewriting(DexMethod method, boolean isInvokeSuper) {
- if (!needsDesugarging) {
- needsDesugarging =
+ if (!needsDesugaring) {
+ needsDesugaring =
interfaceMethodRewriter != null
&& interfaceMethodRewriter.needsRewriting(method, isInvokeSuper, appView);
}
}
private void registerDesugaredLibraryAPIConverter(DexMethod method) {
- if (!needsDesugarging) {
- needsDesugarging =
+ if (!needsDesugaring) {
+ needsDesugaring =
desugaredLibraryAPIConverter != null
&& desugaredLibraryAPIConverter.shouldRewriteInvoke(method);
}
}
private void registerLibraryRetargeting(DexMethod method, boolean b) {
- if (!needsDesugarging) {
- needsDesugarging =
+ if (!needsDesugaring) {
+ needsDesugaring =
desugaredLibraryRetargeter != null
&& desugaredLibraryRetargeter.getRetargetedMethod(method, b) != null;
}
@@ -99,9 +106,7 @@
@Override
public void registerInvokeStatic(DexMethod method) {
- if (!needsDesugarging) {
- needsDesugarging = TwrCloseResourceRewriter.isSynthesizedCloseResourceMethod(method, appView);
- }
+ registerTwrCloseResourceRewriting(method);
registerBackportedMethodRewriting(method);
registerLibraryRetargeting(method, false);
registerInterfaceMethodRewriting(method, false);
@@ -118,7 +123,7 @@
@Override
public void registerInvokeStatic(DexMethod method, boolean itf) {
if (itf) {
- needsDesugarging = true;
+ needsDesugaring = true;
}
registerInvokeStatic(method);
}
@@ -126,7 +131,7 @@
@Override
public void registerCallSite(DexCallSite callSite) {
super.registerCallSite(callSite);
- needsDesugarging = true;
+ needsDesugaring = true;
}
@Override
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 bb3f07c..a8c2e57 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
@@ -52,11 +52,6 @@
return true;
}
- @Override
- public Phase getPhase() {
- return Phase.ONE_TIME;
- }
-
public <E extends Exception> void forEachWaveWithExtension(
ThrowingBiConsumer<ProgramMethod, MethodProcessingId, E> consumer) throws E {
while (!wave.isEmpty()) {
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 3d86936..649edfd 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
@@ -37,14 +37,14 @@
private final AppView<AppInfoWithLiveness> appView;
private final Collection<CodeOptimization> defaultCodeOptimizations;
- private final Map<DexEncodedMethod, Collection<CodeOptimization>> methodsMap;
+ private final Map<DexMethod, Collection<CodeOptimization>> methodsMap;
private final Deque<SortedProgramMethodSet> waves;
private final ProgramMethodSet processed = ProgramMethodSet.create();
private PostMethodProcessor(
AppView<AppInfoWithLiveness> appView,
Collection<CodeOptimization> defaultCodeOptimizations,
- Map<DexEncodedMethod, Collection<CodeOptimization>> methodsMap,
+ Map<DexMethod, Collection<CodeOptimization>> methodsMap,
CallGraph callGraph) {
this.appView = appView;
this.defaultCodeOptimizations = defaultCodeOptimizations;
@@ -53,11 +53,6 @@
}
@Override
- public Phase getPhase() {
- return Phase.POST;
- }
-
- @Override
public boolean shouldApplyCodeRewritings(ProgramMethod method) {
assert !wave.contains(method);
return !processed.contains(method);
@@ -68,7 +63,7 @@
private final Collection<CodeOptimization> defaultCodeOptimizations;
private final LongLivedProgramMethodSetBuilder<?> methodsToReprocess =
LongLivedProgramMethodSetBuilder.createForIdentitySet();
- private final Map<DexEncodedMethod, Collection<CodeOptimization>> optimizationsMap =
+ private final Map<DexMethod, Collection<CodeOptimization>> optimizationsMap =
new IdentityHashMap<>();
Builder(Collection<CodeOptimization> defaultCodeOptimizations) {
@@ -85,7 +80,7 @@
methodsToReprocess.add(method);
optimizationsMap
.computeIfAbsent(
- method.getDefinition(),
+ method.getReference(),
// Optimization order might matter, hence a collection that preserves orderings.
k -> new LinkedHashSet<>())
.addAll(codeOptimizations);
@@ -110,13 +105,11 @@
// according to the graph lens.
public void rewrittenWithLens(AppView<AppInfoWithLiveness> appView, GraphLens applied) {
methodsToReprocess.rewrittenWithLens(appView, applied);
- Map<DexEncodedMethod, Collection<CodeOptimization>> newOptimizationsMap =
- new IdentityHashMap<>();
+ Map<DexMethod, Collection<CodeOptimization>> newOptimizationsMap = new IdentityHashMap<>();
optimizationsMap.forEach(
(method, optimizations) ->
newOptimizationsMap.put(
- appView.graphLens().mapDexEncodedMethod(method, appView, applied),
- optimizations));
+ appView.graphLens().getRenamedMethodSignature(method, applied), optimizations));
optimizationsMap.clear();
optimizationsMap.putAll(newOptimizationsMap);
}
@@ -164,9 +157,13 @@
}
@Override
- public void scheduleMethodForProcessingAfterCurrentWave(ProgramMethod method) {
- super.scheduleMethodForProcessingAfterCurrentWave(method);
- methodsMap.put(method.getDefinition(), defaultCodeOptimizations);
+ protected void prepareForWaveExtensionProcessing() {
+ waveExtension.forEach(
+ method -> {
+ assert !methodsMap.containsKey(method.getReference());
+ methodsMap.put(method.getReference(), defaultCodeOptimizations);
+ });
+ super.prepareForWaveExtensionProcessing();
}
void forEachWaveWithExtension(OptimizationFeedback feedback, ExecutorService executorService)
@@ -182,7 +179,7 @@
wave,
(method, index) -> {
Collection<CodeOptimization> codeOptimizations =
- methodsMap.get(method.getDefinition());
+ methodsMap.get(method.getReference());
assert codeOptimizations != null && !codeOptimizations.isEmpty();
forEachMethod(
method, codeOptimizations, feedback, methodProcessingIds.get(method, index));
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 dd4c0a0..cb288b0 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
@@ -62,8 +62,8 @@
}
@Override
- public Phase getPhase() {
- return Phase.PRIMARY;
+ public boolean isPrimaryMethodProcessor() {
+ return true;
}
@Override
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/BackportedMethodRewriter.java b/src/main/java/com/android/tools/r8/ir/desugar/BackportedMethodRewriter.java
index 40b18c9..994e2ba 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/BackportedMethodRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/BackportedMethodRewriter.java
@@ -7,7 +7,6 @@
import com.android.tools.r8.cf.code.CfInstruction;
import com.android.tools.r8.cf.code.CfInvoke;
import com.android.tools.r8.dex.ApplicationReader;
-import com.android.tools.r8.dex.Constants;
import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.graph.AppInfo;
import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
@@ -17,7 +16,6 @@
import com.android.tools.r8.graph.DexApplication;
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.DexProto;
import com.android.tools.r8.graph.DexString;
import com.android.tools.r8.graph.DexType;
@@ -33,12 +31,16 @@
import com.android.tools.r8.ir.desugar.backports.NumericMethodRewrites;
import com.android.tools.r8.ir.desugar.backports.ObjectsMethodRewrites;
import com.android.tools.r8.ir.desugar.backports.OptionalMethodRewrites;
+import com.android.tools.r8.synthesis.SyntheticNaming;
import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.AndroidApp;
+import com.android.tools.r8.utils.IntBox;
import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.InternalOptions.DesugarState;
import com.android.tools.r8.utils.StringDiagnostic;
import com.android.tools.r8.utils.Timing;
+import com.google.common.hash.Hasher;
+import com.google.common.hash.Hashing;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
@@ -51,6 +53,7 @@
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.function.Consumer;
+import java.util.function.Supplier;
import org.objectweb.asm.Opcodes;
public final class BackportedMethodRewriter {
@@ -128,6 +131,14 @@
}
CfCode code = method.getDefinition().getCode().asCfCode();
ListIterator<CfInstruction> iterator = code.getInstructions().listIterator();
+ // TODO(b/172194101): Make this part of a unique context construction.
+ IntBox nextBackportId = new IntBox();
+ Supplier<String> methodIdSupplier =
+ () -> {
+ Hasher hasher = Hashing.sha256().newHasher();
+ method.getReference().hash(hasher);
+ return "$" + hasher.hash().toString() + "$" + nextBackportId.getAndIncrement();
+ };
boolean replaced = false;
while (iterator.hasNext()) {
CfInvoke invoke = iterator.next().asInvoke();
@@ -144,7 +155,7 @@
iterator = mutableInstructions.listIterator(iterator.previousIndex());
iterator.next();
}
- provider.rewriteInvoke(invoke, iterator, method.getHolder(), appInfo, consumer);
+ provider.rewriteInvoke(invoke, iterator, method, appInfo, consumer, methodIdSupplier);
replaced = true;
}
}
@@ -1348,9 +1359,10 @@
public abstract void rewriteInvoke(
CfInvoke invoke,
ListIterator<CfInstruction> iterator,
- DexProgramClass context,
+ ProgramMethod context,
AppInfoWithClassHierarchy appInfo,
- Consumer<ProgramMethod> registerSynthesizedMethod);
+ Consumer<ProgramMethod> registerSynthesizedMethod,
+ Supplier<String> methodIdProvider);
}
private static final class InvokeRewriter extends MethodProvider {
@@ -1366,9 +1378,10 @@
public void rewriteInvoke(
CfInvoke invoke,
ListIterator<CfInstruction> iterator,
- DexProgramClass context,
+ ProgramMethod context,
AppInfoWithClassHierarchy appInfo,
- Consumer<ProgramMethod> registerSynthesizedMethod) {
+ Consumer<ProgramMethod> registerSynthesizedMethod,
+ Supplier<String> methodIdProvider) {
rewriter.rewrite(invoke, iterator, appInfo.dexItemFactory());
}
}
@@ -1392,31 +1405,33 @@
public void rewriteInvoke(
CfInvoke invoke,
ListIterator<CfInstruction> iterator,
- DexProgramClass context,
+ ProgramMethod context,
AppInfoWithClassHierarchy appInfo,
- Consumer<ProgramMethod> registerSynthesizedMethod) {
- ProgramMethod method = getSyntheticMethod(context, appInfo);
+ Consumer<ProgramMethod> registerSynthesizedMethod,
+ Supplier<String> methodIdProvider) {
+ ProgramMethod method = getSyntheticMethod(context, methodIdProvider, appInfo);
registerSynthesizedMethod.accept(method);
iterator.remove();
iterator.add(new CfInvoke(Opcodes.INVOKESTATIC, method.getReference(), false));
}
private ProgramMethod getSyntheticMethod(
- DexProgramClass context, AppInfoWithClassHierarchy appInfo) {
+ ProgramMethod context,
+ Supplier<String> methodIdProvider,
+ AppInfoWithClassHierarchy appInfo) {
return appInfo
.getSyntheticItems()
.createMethod(
+ SyntheticNaming.SyntheticKind.BACKPORT,
context,
appInfo.dexItemFactory(),
builder ->
builder
.setProto(getProto(appInfo.dexItemFactory()))
- .setAccessFlags(
- MethodAccessFlags.fromSharedAccessFlags(
- Constants.ACC_PUBLIC | Constants.ACC_STATIC | Constants.ACC_SYNTHETIC,
- false))
+ .setAccessFlags(MethodAccessFlags.createPublicStaticSynthetic())
.setCode(
- methodSig -> generateTemplateMethod(appInfo.app().options, methodSig)));
+ methodSig -> generateTemplateMethod(appInfo.app().options, methodSig)),
+ methodIdProvider);
}
public DexProto getProto(DexItemFactory itemFactory) {
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/D8NestBasedAccessDesugaring.java b/src/main/java/com/android/tools/r8/ir/desugar/D8NestBasedAccessDesugaring.java
deleted file mode 100644
index 787915f..0000000
--- a/src/main/java/com/android/tools/r8/ir/desugar/D8NestBasedAccessDesugaring.java
+++ /dev/null
@@ -1,173 +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.ir.desugar;
-
-import com.android.tools.r8.graph.AppView;
-import com.android.tools.r8.graph.DexApplication;
-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.DexMethod;
-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.ir.code.BasicBlock;
-import com.android.tools.r8.ir.code.FieldInstruction;
-import com.android.tools.r8.ir.code.IRCode;
-import com.android.tools.r8.ir.code.Instruction;
-import com.android.tools.r8.ir.code.InstructionListIterator;
-import com.android.tools.r8.ir.code.InvokeDirect;
-import com.android.tools.r8.ir.code.InvokeMethod;
-import com.android.tools.r8.ir.code.InvokeStatic;
-import com.android.tools.r8.ir.code.Value;
-import com.android.tools.r8.ir.conversion.IRConverter;
-import com.android.tools.r8.utils.ThreadUtils;
-import com.android.tools.r8.utils.collections.SortedProgramMethodSet;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.List;
-import java.util.ListIterator;
-import java.util.Map;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.ExecutionException;
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Future;
-
-// Summary:
-// - Process all methods compiled rewriting nest based access (Methods processed concurrently).
-// - Process classes on class path in reachable nests to find bridges to add
-// in Program classes (Nests processed concurrently).
-// - Add bridges and nest constructor class (Sequential).
-// - Optimize bridges (Bridges processed concurrently).
-public class D8NestBasedAccessDesugaring extends NestBasedAccessDesugaring {
-
- // Maps a nest host to a class met which has that nest host.
- // The value is used because the nest host might be missing.
- private final Map<DexType, DexProgramClass> metNestHosts = new ConcurrentHashMap<>();
-
- public D8NestBasedAccessDesugaring(AppView<?> appView) {
- super(appView);
- }
-
- public void rewriteNestBasedAccesses(ProgramMethod method, IRCode code, AppView<?> appView) {
- if (!method.getHolder().isInANest()) {
- return;
- }
- metNestHosts.put(method.getHolder().getNestHost(), method.getHolder());
-
- ListIterator<BasicBlock> blocks = code.listIterator();
- while (blocks.hasNext()) {
- BasicBlock block = blocks.next();
- InstructionListIterator instructions = block.listIterator(code);
- while (instructions.hasNext()) {
- Instruction instruction = instructions.next();
- if (instruction.isInvokeMethod()) {
- InvokeMethod invokeMethod = instruction.asInvokeMethod();
- DexMethod invokedMethod = invokeMethod.getInvokedMethod();
- if (!invokedMethod.holder.isClassType()) {
- continue;
- }
- // Since we only need to desugar accesses to private methods, and all accesses to private
- // methods must be accessing the private method directly on its holder, we can lookup the
- // method on the holder instead of resolving the method.
- DexClass holder = appView.definitionForHolder(invokedMethod);
- DexEncodedMethod definition = invokedMethod.lookupOnClass(holder);
- if (definition != null && invokeRequiresRewriting(definition, method)) {
- DexMethod bridge = ensureInvokeBridge(definition);
- if (definition.isInstanceInitializer()) {
- instructions.previous();
- Value extraNullValue =
- instructions.insertConstNullInstruction(code, appView.options());
- instructions.next();
- List<Value> parameters = new ArrayList<>(invokeMethod.arguments());
- parameters.add(extraNullValue);
- instructions.replaceCurrentInstruction(
- new InvokeDirect(bridge, invokeMethod.outValue(), parameters));
- } else {
- instructions.replaceCurrentInstruction(
- new InvokeStatic(bridge, invokeMethod.outValue(), invokeMethod.arguments()));
- }
- }
- } else if (instruction.isFieldInstruction()) {
- // Since we only need to desugar accesses to private fields, and all accesses to private
- // fields must be accessing the private field directly on its holder, we can lookup the
- // field on the holder instead of resolving the field.
- FieldInstruction fieldInstruction = instruction.asFieldInstruction();
- DexClass holder = appView.definitionForHolder(fieldInstruction.getField());
- DexEncodedField field = fieldInstruction.getField().lookupOnClass(holder);
- if (field != null && fieldAccessRequiresRewriting(field, method)) {
- if (instruction.isInstanceGet() || instruction.isStaticGet()) {
- DexMethod bridge = ensureFieldAccessBridge(field, true);
- instructions.replaceCurrentInstruction(
- new InvokeStatic(bridge, instruction.outValue(), instruction.inValues()));
- } else {
- assert instruction.isInstancePut() || instruction.isStaticPut();
- DexMethod bridge = ensureFieldAccessBridge(field, false);
- instructions.replaceCurrentInstruction(
- new InvokeStatic(bridge, instruction.outValue(), instruction.inValues()));
- }
- }
- }
- }
- }
- }
-
- private void processNestsConcurrently(ExecutorService executorService) throws ExecutionException {
- List<Future<?>> futures = new ArrayList<>();
- for (DexProgramClass clazz : metNestHosts.values()) {
- futures.add(asyncProcessNest(clazz, executorService));
- }
- ThreadUtils.awaitFutures(futures);
- }
-
- private void addDeferredBridges() {
- addDeferredBridges(bridges.values());
- addDeferredBridges(getFieldBridges.values());
- addDeferredBridges(putFieldBridges.values());
- }
-
- private void addDeferredBridges(Collection<ProgramMethod> bridges) {
- for (ProgramMethod bridge : bridges) {
- bridge.getHolder().addMethod(bridge.getDefinition());
- }
- }
-
- private void optimizeDeferredBridgesConcurrently(
- ExecutorService executorService, IRConverter converter) throws ExecutionException {
- SortedProgramMethodSet methods = SortedProgramMethodSet.create();
- methods.addAll(bridges.values());
- methods.addAll(getFieldBridges.values());
- methods.addAll(putFieldBridges.values());
- converter.processMethodsConcurrently(methods, executorService);
- }
-
- public void desugarNestBasedAccess(
- DexApplication.Builder<?> builder, ExecutorService executorService, IRConverter converter)
- throws ExecutionException {
- if (metNestHosts.isEmpty()) {
- return;
- }
- processNestsConcurrently(executorService);
- addDeferredBridges();
- synthesizeNestConstructor(builder);
- optimizeDeferredBridgesConcurrently(executorService, converter);
- }
-
- // In D8, programClass are processed on the fly so they do not need to be processed again here.
- @Override
- protected boolean shouldProcessClassInNest(DexClass clazz, List<DexType> nest) {
- return clazz.isClasspathClass();
- }
-
- @Override
- void reportMissingNestHost(DexClass clazz) {
- appView.options().nestDesugaringWarningMissingNestHost(clazz);
- }
-
- @Override
- void reportIncompleteNest(List<DexType> nest) {
- appView.options().nestDesugaringWarningIncompleteNest(nest, appView);
- }
-}
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 cf85276..860af39 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
@@ -178,7 +178,7 @@
|| definition.isLibraryMethodOverride().isFalse()) {
return false;
}
- if (!appView.rewritePrefix.hasRewrittenTypeInSignature(definition.proto(), appView)
+ if (!appView.rewritePrefix.hasRewrittenTypeInSignature(definition.getProto(), appView)
|| appView
.options()
.desugaredLibraryConfiguration
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 3dc74ac..a6606ed 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
@@ -43,6 +43,7 @@
import com.android.tools.r8.ir.code.InvokeStatic;
import com.android.tools.r8.ir.conversion.IRConverter;
import com.android.tools.r8.origin.SynthesizedOrigin;
+import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.StringDiagnostic;
import com.android.tools.r8.utils.WorkList;
import com.android.tools.r8.utils.collections.DexClassAndMethodSet;
@@ -64,6 +65,7 @@
public class DesugaredLibraryRetargeter {
+ private static final String RETARGET_PACKAGE = "retarget/";
public static final String DESUGAR_LIB_RETARGET_CLASS_NAME_PREFIX =
"$r8$retargetLibraryMember$virtualDispatch";
@@ -76,14 +78,27 @@
// Non final virtual library methods requiring generation of emulated dispatch.
private final DexClassAndMethodSet emulatedDispatchMethods = DexClassAndMethodSet.create();
+ private final String packageAndClassDescriptorPrefix;
+
public DesugaredLibraryRetargeter(AppView<?> appView) {
this.appView = appView;
+ packageAndClassDescriptorPrefix =
+ getRetargetPackageAndClassPrefixDescriptor(appView.options().desugaredLibraryConfiguration);
if (appView.options().desugaredLibraryConfiguration.getRetargetCoreLibMember().isEmpty()) {
return;
}
new RetargetingSetup().setUpRetargeting();
}
+ public static boolean isRetargetType(DexType type, InternalOptions options) {
+ if (options.desugaredLibraryConfiguration == null) {
+ return false;
+ }
+ return type.toDescriptorString()
+ .startsWith(
+ getRetargetPackageAndClassPrefixDescriptor(options.desugaredLibraryConfiguration));
+ }
+
public static void checkForAssumedLibraryTypes(AppView<?> appView) {
Map<DexString, Map<DexType, DexType>> retargetCoreLibMember =
appView.options().desugaredLibraryConfiguration.getRetargetCoreLibMember();
@@ -347,8 +362,10 @@
private class RetargetingSetup {
private void setUpRetargeting() {
+ DesugaredLibraryConfiguration desugaredLibraryConfiguration =
+ appView.options().desugaredLibraryConfiguration;
Map<DexString, Map<DexType, DexType>> retargetCoreLibMember =
- appView.options().desugaredLibraryConfiguration.getRetargetCoreLibMember();
+ desugaredLibraryConfiguration.getRetargetCoreLibMember();
for (DexString methodName : retargetCoreLibMember.keySet()) {
for (DexType inType : retargetCoreLibMember.get(methodName).keySet()) {
DexClass typeClass = appView.definitionFor(inType);
@@ -377,6 +394,22 @@
}
}
}
+ if (desugaredLibraryConfiguration.isLibraryCompilation()) {
+ // TODO(b/177977763): This is only a workaround rewriting invokes of j.u.Arrays.deepEquals0
+ // to j.u.DesugarArrays.deepEquals0.
+ DexItemFactory itemFactory = appView.options().dexItemFactory();
+ DexString name = itemFactory.createString("deepEquals0");
+ DexProto proto =
+ itemFactory.createProto(
+ itemFactory.booleanType, itemFactory.objectType, itemFactory.objectType);
+ DexMethod source =
+ itemFactory.createMethod(
+ itemFactory.createType(itemFactory.arraysDescriptor), proto, name);
+ DexMethod target =
+ itemFactory.createMethod(
+ itemFactory.createType("Ljava/util/DesugarArrays;"), proto, name);
+ retargetLibraryMember.put(source, target);
+ }
}
private boolean isEmulatedInterfaceDispatch(DexClassAndMethod method) {
@@ -668,14 +701,17 @@
return dispatchTypeFor(method, "dispatchHolder");
}
+ private static String getRetargetPackageAndClassPrefixDescriptor(
+ DesugaredLibraryConfiguration config) {
+ return "L"
+ + config.getSynthesizedLibraryClassesPackagePrefix()
+ + RETARGET_PACKAGE
+ + DESUGAR_LIB_RETARGET_CLASS_NAME_PREFIX;
+ }
+
private DexType dispatchTypeFor(DexClassAndMethod method, String suffix) {
String descriptor =
- "L"
- + appView
- .options()
- .desugaredLibraryConfiguration
- .getSynthesizedLibraryClassesPackagePrefix()
- + DESUGAR_LIB_RETARGET_CLASS_NAME_PREFIX
+ packageAndClassDescriptorPrefix
+ '$'
+ method.getHolderType().getName()
+ '$'
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 1529d2f..d2225e6 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
@@ -13,7 +13,6 @@
import com.android.tools.r8.DesugarGraphConsumer;
import com.android.tools.r8.cf.CfVersion;
-import com.android.tools.r8.dex.Constants;
import com.android.tools.r8.errors.CompilationError;
import com.android.tools.r8.errors.Unimplemented;
import com.android.tools.r8.graph.AppInfo;
@@ -24,6 +23,7 @@
import com.android.tools.r8.graph.DexCallSite;
import com.android.tools.r8.graph.DexClass;
import com.android.tools.r8.graph.DexClassAndMethod;
+import com.android.tools.r8.graph.DexClasspathClass;
import com.android.tools.r8.graph.DexEncodedField;
import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.graph.DexItemFactory;
@@ -36,9 +36,13 @@
import com.android.tools.r8.graph.DexValue;
import com.android.tools.r8.graph.GenericSignature;
import com.android.tools.r8.graph.GenericSignature.ClassSignature;
+import com.android.tools.r8.graph.GenericSignature.MethodTypeSignature;
import com.android.tools.r8.graph.MethodAccessFlags;
+import com.android.tools.r8.graph.MethodCollection;
+import com.android.tools.r8.graph.ParameterAnnotationsList;
import com.android.tools.r8.graph.ProgramMethod;
import com.android.tools.r8.graph.ResolutionResult.SingleResolutionResult;
+import com.android.tools.r8.ir.analysis.type.TypeAnalysis;
import com.android.tools.r8.ir.code.BasicBlock;
import com.android.tools.r8.ir.code.IRCode;
import com.android.tools.r8.ir.code.Instruction;
@@ -49,18 +53,24 @@
import com.android.tools.r8.ir.code.InvokeMethodWithReceiver;
import com.android.tools.r8.ir.code.InvokeStatic;
import com.android.tools.r8.ir.code.InvokeSuper;
+import com.android.tools.r8.ir.code.Value;
import com.android.tools.r8.ir.conversion.IRConverter;
+import com.android.tools.r8.ir.conversion.MethodProcessingId;
+import com.android.tools.r8.ir.conversion.MethodProcessor;
import com.android.tools.r8.ir.desugar.DefaultMethodsHelper.Collection;
import com.android.tools.r8.ir.desugar.InterfaceProcessor.InterfaceProcessorNestedGraphLens;
+import com.android.tools.r8.ir.optimize.UtilityMethodsForCodeOptimizations;
+import com.android.tools.r8.ir.optimize.UtilityMethodsForCodeOptimizations.UtilityMethodForCodeOptimizations;
import com.android.tools.r8.ir.synthetic.ForwardMethodBuilder;
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;
import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.IterableUtils;
-import com.android.tools.r8.utils.ObjectUtils;
import com.android.tools.r8.utils.Pair;
import com.android.tools.r8.utils.StringDiagnostic;
import com.android.tools.r8.utils.collections.SortedProgramMethodSet;
@@ -251,15 +261,21 @@
// Rewrites the references to static and default interface methods.
// NOTE: can be called for different methods concurrently.
- public void rewriteMethodReferences(IRCode code) {
+ public void rewriteMethodReferences(
+ IRCode code, MethodProcessor methodProcessor, MethodProcessingId methodProcessingId) {
ProgramMethod context = code.context();
if (synthesizedMethods.contains(context)) {
return;
}
+ Set<Value> affectedValues = Sets.newIdentityHashSet();
+ Set<BasicBlock> blocksToRemove = Sets.newIdentityHashSet();
ListIterator<BasicBlock> blocks = code.listIterator();
while (blocks.hasNext()) {
BasicBlock block = blocks.next();
+ if (blocksToRemove.contains(block)) {
+ continue;
+ }
InstructionListIterator instructions = block.listIterator(code);
while (instructions.hasNext()) {
Instruction instruction = instructions.next();
@@ -271,7 +287,15 @@
rewriteInvokeDirect(instruction.asInvokeDirect(), instructions, context);
break;
case INVOKE_STATIC:
- rewriteInvokeStatic(instruction.asInvokeStatic(), instructions, context);
+ rewriteInvokeStatic(
+ instruction.asInvokeStatic(),
+ code,
+ blocks,
+ instructions,
+ affectedValues,
+ blocksToRemove,
+ methodProcessor,
+ methodProcessingId);
break;
case INVOKE_SUPER:
rewriteInvokeSuper(instruction.asInvokeSuper(), instructions, context);
@@ -287,6 +311,14 @@
}
}
}
+
+ code.removeBlocks(blocksToRemove);
+
+ if (!affectedValues.isEmpty()) {
+ new TypeAnalysis(appView).narrowing(affectedValues);
+ }
+
+ assert code.isConsistentSSA();
}
private void rewriteInvokeCustom(InvokeCustom invoke, ProgramMethod context) {
@@ -327,16 +359,16 @@
getMethodOrigin(context.getReference()));
}
- DexEncodedMethod directTarget = clazz.lookupMethod(method);
+ DexClassAndMethod directTarget = clazz.lookupClassMethod(method);
if (directTarget != null) {
// This can be a private instance method call. Note that the referenced
// method is expected to be in the current class since it is private, but desugaring
// may move some methods or their code into other classes.
instructions.replaceCurrentInstruction(
new InvokeStatic(
- directTarget.isPrivateMethod()
- ? privateAsMethodOfCompanionClass(method)
- : defaultAsMethodOfCompanionClass(method),
+ directTarget.getDefinition().isPrivateMethod()
+ ? privateAsMethodOfCompanionClass(directTarget)
+ : defaultAsMethodOfCompanionClass(directTarget),
invoke.outValue(),
invoke.arguments()));
} else {
@@ -347,7 +379,7 @@
// This is a invoke-direct call to a virtual method.
instructions.replaceCurrentInstruction(
new InvokeStatic(
- defaultAsMethodOfCompanionClass(virtualTarget.getDefinition().method),
+ defaultAsMethodOfCompanionClass(virtualTarget),
invoke.outValue(),
invoke.arguments()));
} else {
@@ -359,13 +391,21 @@
}
private void rewriteInvokeStatic(
- InvokeStatic invoke, InstructionListIterator instructions, ProgramMethod context) {
+ InvokeStatic invoke,
+ IRCode code,
+ ListIterator<BasicBlock> blockIterator,
+ InstructionListIterator instructions,
+ Set<Value> affectedValues,
+ Set<BasicBlock> blocksToRemove,
+ MethodProcessor methodProcessor,
+ MethodProcessingId methodProcessingId) {
DexMethod invokedMethod = invoke.getInvokedMethod();
if (appView.getSyntheticItems().isPendingSynthetic(invokedMethod.holder)) {
// We did not create this code yet, but it will not require rewriting.
return;
}
+ ProgramMethod context = code.context();
DexClass clazz = appView.definitionFor(invokedMethod.holder, context);
if (clazz == null) {
// NOTE: leave unchanged those calls to undefined targets. This may lead to runtime
@@ -406,17 +446,13 @@
appView
.getSyntheticItems()
.createMethod(
+ SyntheticNaming.SyntheticKind.STATIC_INTERFACE_CALL,
context.getHolder(),
factory,
syntheticMethodBuilder ->
syntheticMethodBuilder
.setProto(invokedMethod.proto)
- .setAccessFlags(
- MethodAccessFlags.fromSharedAccessFlags(
- Constants.ACC_PUBLIC
- | Constants.ACC_STATIC
- | Constants.ACC_SYNTHETIC,
- false))
+ .setAccessFlags(MethodAccessFlags.createPublicStaticSynthetic())
.setCode(
m ->
ForwardMethodBuilder.builder(factory)
@@ -436,13 +472,43 @@
// When leaving static interface method invokes upgrade the class file version.
context.getDefinition().upgradeClassFileVersion(CfVersion.V1_8);
}
- } else {
+ return;
+ }
+
+ SingleResolutionResult resolutionResult =
+ appView
+ .appInfoForDesugaring()
+ .resolveMethodOnInterface(clazz, invokedMethod)
+ .asSingleResolution();
+ if (resolutionResult != null && resolutionResult.getResolvedMethod().isStatic()) {
instructions.replaceCurrentInstruction(
new InvokeStatic(
- staticAsMethodOfCompanionClass(invokedMethod),
+ staticAsMethodOfCompanionClass(resolutionResult.getResolutionPair()),
invoke.outValue(),
invoke.arguments()));
+ return;
}
+
+ // Replace by throw new IncompatibleClassChangeError/NoSuchMethodError.
+ UtilityMethodForCodeOptimizations throwMethod =
+ resolutionResult == null
+ ? UtilityMethodsForCodeOptimizations.synthesizeThrowNoSuchMethodErrorMethod(
+ appView, context, methodProcessingId)
+ : UtilityMethodsForCodeOptimizations.synthesizeThrowIncompatibleClassChangeErrorMethod(
+ appView, context, methodProcessingId);
+ throwMethod.optimize(methodProcessor);
+
+ InvokeStatic throwInvoke =
+ InvokeStatic.builder()
+ .setMethod(throwMethod.getMethod())
+ .setFreshOutValue(appView, code)
+ .setPosition(invoke)
+ .build();
+ instructions.previous();
+ instructions.add(throwInvoke);
+ instructions.next();
+ instructions.replaceCurrentInstructionWithThrow(
+ appView, code, blockIterator, throwInvoke.outValue(), blocksToRemove, affectedValues);
}
private void rewriteInvokeSuper(
@@ -470,7 +536,7 @@
DexMethod amendedMethod = amendDefaultMethod(context.getHolder(), invokedMethod);
instructions.replaceCurrentInstruction(
new InvokeStatic(
- defaultAsMethodOfCompanionClass(amendedMethod),
+ defaultAsMethodOfCompanionClass(amendedMethod, appView.dexItemFactory()),
invoke.outValue(),
invoke.arguments()));
} else {
@@ -484,7 +550,7 @@
if (holder.isLibraryClass() && holder.isInterface()) {
instructions.replaceCurrentInstruction(
new InvokeStatic(
- defaultAsMethodOfCompanionClass(target.getReference(), factory),
+ defaultAsMethodOfCompanionClass(target),
invoke.outValue(),
invoke.arguments()));
}
@@ -504,9 +570,7 @@
DexMethod retargetMethod =
options.desugaredLibraryConfiguration.retargetMethod(superTarget, appView);
if (retargetMethod == null) {
- DexMethod originalCompanionMethod =
- instanceAsMethodOfCompanionClass(
- superTarget.getReference(), DEFAULT_METHOD_PREFIX, factory);
+ DexMethod originalCompanionMethod = defaultAsMethodOfCompanionClass(superTarget);
DexMethod companionMethod =
factory.createMethod(
getCompanionClassType(emulatedItf),
@@ -892,12 +956,11 @@
// Represent a static interface method as a method of companion class.
final DexMethod staticAsMethodOfCompanionClass(DexClassAndMethod method) {
- return staticAsMethodOfCompanionClass(method.getReference());
- }
-
- final DexMethod staticAsMethodOfCompanionClass(DexMethod method) {
- // No changes for static methods.
- return factory.createMethod(getCompanionClassType(method.holder), method.proto, method.name);
+ DexItemFactory dexItemFactory = appView.dexItemFactory();
+ DexType companionClassType = getCompanionClassType(method.getHolderType(), dexItemFactory);
+ DexMethod rewritten = method.getReference().withHolder(companionClassType, dexItemFactory);
+ recordCompanionClassReference(appView, method, rewritten);
+ return rewritten;
}
private static DexMethod instanceAsMethodOfCompanionClass(
@@ -930,12 +993,11 @@
return instanceAsMethodOfCompanionClass(method, DEFAULT_METHOD_PREFIX, factory);
}
- DexMethod defaultAsMethodOfCompanionClass(DexMethod method) {
- return defaultAsMethodOfCompanionClass(method, factory);
- }
-
- DexMethod defaultAsMethodOfCompanionClass(DexClassAndMethod method) {
- return defaultAsMethodOfCompanionClass(method.getReference(), factory);
+ final DexMethod defaultAsMethodOfCompanionClass(DexClassAndMethod method) {
+ DexItemFactory dexItemFactory = appView.dexItemFactory();
+ DexMethod rewritten = defaultAsMethodOfCompanionClass(method.getReference(), dexItemFactory);
+ recordCompanionClassReference(appView, method, rewritten);
+ return rewritten;
}
// Represent a private instance interface method as a method of companion class.
@@ -944,8 +1006,57 @@
return instanceAsMethodOfCompanionClass(method, PRIVATE_METHOD_PREFIX, factory);
}
- DexMethod privateAsMethodOfCompanionClass(DexMethod method) {
- return privateAsMethodOfCompanionClass(method, factory);
+ private DexMethod privateAsMethodOfCompanionClass(DexClassAndMethod method) {
+ return privateAsMethodOfCompanionClass(method.getReference(), factory);
+ }
+
+ private static void recordCompanionClassReference(
+ AppView<?> appView, DexClassAndMethod method, DexMethod rewritten) {
+ // If the interface class is a program class, we shouldn't need to synthesize the companion
+ // class on the classpath.
+ if (method.getHolder().isProgramClass()) {
+ return;
+ }
+
+ // If the companion class does not exist, then synthesize it on the classpath.
+ DexClass companionClass = appView.definitionFor(rewritten.getHolderType());
+ if (companionClass == null) {
+ companionClass =
+ synthesizeEmptyCompanionClass(appView, rewritten.getHolderType(), method.getHolder());
+ }
+
+ // If the companion class is a classpath class, then synthesize the companion class method if it
+ // does not exist.
+ if (companionClass.isClasspathClass()) {
+ synthetizeCompanionClassMethodIfNotPresent(companionClass.asClasspathClass(), rewritten);
+ }
+ }
+
+ private static DexClasspathClass synthesizeEmptyCompanionClass(
+ AppView<?> appView, DexType type, DexClass context) {
+ return appView
+ .getSyntheticItems()
+ .createClasspathClass(
+ SyntheticKind.COMPANION_CLASS, type, context, appView.dexItemFactory());
+ }
+
+ private static void synthetizeCompanionClassMethodIfNotPresent(
+ DexClasspathClass companionClass, DexMethod method) {
+ MethodCollection methodCollection = companionClass.getMethodCollection();
+ synchronized (methodCollection) {
+ if (methodCollection.getMethod(method) == null) {
+ boolean d8R8Synthesized = true;
+ methodCollection.addDirectMethod(
+ new DexEncodedMethod(
+ method,
+ MethodAccessFlags.createPublicStaticSynthetic(),
+ MethodTypeSignature.noSignature(),
+ DexAnnotationSet.empty(),
+ ParameterAnnotationsList.empty(),
+ DexEncodedMethod.buildEmptyThrowingCfCode(method),
+ d8R8Synthesized));
+ }
+ }
}
private void renameEmulatedInterfaces() {
@@ -1128,6 +1239,7 @@
Builder<?> builder, Flavor flavour, Consumer<ProgramMethod> newSynthesizedMethodConsumer) {
ClassProcessor processor = new ClassProcessor(appView, this, newSynthesizedMethodConsumer);
// First we compute all desugaring *without* introducing forwarding methods.
+ assert appView.getSyntheticItems().verifyNonLegacySyntheticsAreCommitted();
for (DexProgramClass clazz : builder.getProgramClasses()) {
if (shouldProcess(clazz, flavour, false)) {
if (appView.isAlreadyLibraryDesugared(clazz)) {
@@ -1165,11 +1277,8 @@
|| missing.isD8R8SynthesizedClassType()
|| isCompanionClassType(missing)
|| emulatedInterfaces.containsValue(missing)
- || ObjectUtils.getBooleanOrElse(
- options.getProguardConfiguration(),
- configuration -> configuration.getDontWarnPatterns().matches(missing),
- false)
- || options.desugaredLibraryConfiguration.getCustomConversions().containsValue(missing);
+ || options.desugaredLibraryConfiguration.getCustomConversions().containsValue(missing)
+ || appView.getDontWarnConfiguration().matches(missing);
}
void warnMissingInterface(DexClass classToDesugar, DexClass implementing, DexType missing) {
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/InterfaceProcessor.java b/src/main/java/com/android/tools/r8/ir/desugar/InterfaceProcessor.java
index c2db31a..e40251c 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/InterfaceProcessor.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/InterfaceProcessor.java
@@ -220,23 +220,28 @@
List<DexEncodedMethod> companionMethods,
InterfaceProcessorNestedGraphLens.Builder graphLensBuilder) {
List<DexEncodedMethod> newVirtualMethods = new ArrayList<>();
- for (DexEncodedMethod virtual : iface.virtualMethods()) {
+ for (ProgramMethod method : iface.virtualProgramMethods()) {
+ DexEncodedMethod virtual = method.getDefinition();
if (rewriter.isDefaultMethod(virtual)) {
if (!canMoveToCompanionClass(virtual)) {
- throw new CompilationError("One or more instruction is preventing default interface "
- + "method from being desugared: " + virtual.method.toSourceString(), iface.origin);
+ throw new CompilationError(
+ "One or more instruction is preventing default interface "
+ + "method from being desugared: "
+ + method.toSourceString(),
+ iface.origin);
}
// Create a new method in a companion class to represent default method implementation.
- DexMethod companionMethod = rewriter.defaultAsMethodOfCompanionClass(virtual.method);
+ DexMethod companionMethod = rewriter.defaultAsMethodOfCompanionClass(method);
Code code = virtual.getCode();
if (code == null) {
- throw new CompilationError("Code is missing for default "
- + "interface method: " + virtual.method.toSourceString(), iface.origin);
+ throw new CompilationError(
+ "Code is missing for default " + "interface method: " + method.toSourceString(),
+ iface.origin);
}
- MethodAccessFlags newFlags = virtual.accessFlags.copy();
+ MethodAccessFlags newFlags = method.getAccessFlags().copy();
newFlags.promoteToStatic();
DexEncodedMethod.setDebugInfoWithFakeThisParameter(
code, companionMethod.getArity(), appView);
@@ -252,7 +257,7 @@
implMethod.copyMetadata(virtual);
virtual.setDefaultInterfaceMethodImplementation(implMethod);
companionMethods.add(implMethod);
- graphLensBuilder.recordCodeMovedToCompanionClass(virtual.method, implMethod.method);
+ graphLensBuilder.recordCodeMovedToCompanionClass(method.getReference(), implMethod.method);
}
// Remove bridge methods.
@@ -272,12 +277,13 @@
List<DexEncodedMethod> companionMethods,
InterfaceProcessorNestedGraphLens.Builder graphLensBuilder) {
DexEncodedMethod clinit = null;
- for (DexEncodedMethod method : iface.directMethods()) {
- if (method.isClassInitializer()) {
- clinit = method;
+ for (ProgramMethod method : iface.directProgramMethods()) {
+ DexEncodedMethod definition = method.getDefinition();
+ if (definition.isClassInitializer()) {
+ clinit = definition;
continue;
}
- if (method.isInstanceInitializer()) {
+ if (definition.isInstanceInitializer()) {
assert false
: "Unexpected interface instance initializer: "
+ method.getReference().toSourceString();
@@ -291,24 +297,24 @@
}
DexMethod oldMethod = method.getReference();
- if (isStaticMethod(method)) {
+ if (isStaticMethod(definition)) {
assert originalFlags.isPrivate() || originalFlags.isPublic()
: "Static interface method "
+ method.toSourceString()
+ " is expected to "
+ "either be public or private in "
+ iface.origin;
- DexMethod companionMethod = rewriter.staticAsMethodOfCompanionClass(oldMethod);
+ DexMethod companionMethod = rewriter.staticAsMethodOfCompanionClass(method);
DexEncodedMethod implMethod =
new DexEncodedMethod(
companionMethod,
newFlags,
- method.getGenericSignature(),
- method.annotations(),
- method.parameterAnnotationsList,
- method.getCode(),
+ definition.getGenericSignature(),
+ definition.annotations(),
+ definition.parameterAnnotationsList,
+ definition.getCode(),
true);
- implMethod.copyMetadata(method);
+ implMethod.copyMetadata(definition);
companionMethods.add(implMethod);
graphLensBuilder.move(oldMethod, companionMethod);
continue;
@@ -318,9 +324,10 @@
newFlags.promoteToStatic();
- DexMethod companionMethod = rewriter.privateAsMethodOfCompanionClass(oldMethod);
+ DexMethod companionMethod =
+ rewriter.privateAsMethodOfCompanionClass(oldMethod, appView.dexItemFactory());
- Code code = method.getCode();
+ Code code = definition.getCode();
if (code == null) {
throw new CompilationError(
"Code is missing for private instance "
@@ -333,12 +340,12 @@
new DexEncodedMethod(
companionMethod,
newFlags,
- method.getGenericSignature(),
- method.annotations(),
- method.parameterAnnotationsList,
+ definition.getGenericSignature(),
+ definition.annotations(),
+ definition.parameterAnnotationsList,
code,
true);
- implMethod.copyMetadata(method);
+ implMethod.copyMetadata(definition);
companionMethods.add(implMethod);
graphLensBuilder.move(oldMethod, companionMethod);
}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/LambdaClass.java b/src/main/java/com/android/tools/r8/ir/desugar/LambdaClass.java
index a50581e..477d8d8 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/LambdaClass.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/LambdaClass.java
@@ -8,7 +8,6 @@
import com.android.tools.r8.errors.Unimplemented;
import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.graph.AppView;
-import com.android.tools.r8.graph.ClassAccessFlags;
import com.android.tools.r8.graph.DexAnnotationSet;
import com.android.tools.r8.graph.DexEncodedField;
import com.android.tools.r8.graph.DexEncodedMethod;
@@ -20,10 +19,8 @@
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.FieldAccessFlags;
-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.MethodAccessFlags;
@@ -37,18 +34,11 @@
import com.android.tools.r8.ir.optimize.info.OptimizationFeedbackSimple;
import com.android.tools.r8.ir.synthetic.ForwardMethodSourceCode;
import com.android.tools.r8.ir.synthetic.SynthesizedCode;
-import com.android.tools.r8.origin.SynthesizedOrigin;
+import com.android.tools.r8.synthesis.SyntheticProgramClassBuilder;
import com.android.tools.r8.utils.OptionalBool;
-import com.google.common.base.Suppliers;
-import com.google.common.primitives.Longs;
-import java.nio.ByteBuffer;
import java.util.ArrayList;
-import java.util.Collection;
import java.util.Collections;
import java.util.List;
-import java.util.concurrent.atomic.AtomicBoolean;
-import java.util.function.Supplier;
-import java.util.zip.CRC32;
/**
* Represents lambda class generated for a lambda descriptor in context of lambda instantiation
@@ -77,31 +67,27 @@
final DexMethod classConstructor;
public final DexField lambdaField;
public final Target target;
- public final AtomicBoolean addToMainDexList = new AtomicBoolean(false);
- private final Collection<DexProgramClass> synthesizedFrom = new ArrayList<>(1);
- private final Supplier<DexProgramClass> lazyDexClass =
- Suppliers.memoize(this::synthesizeLambdaClass); // NOTE: thread-safe.
+
+ // Considered final but is set after due to circularity in allocation.
+ private DexProgramClass clazz = null;
LambdaClass(
+ SyntheticProgramClassBuilder builder,
AppView<?> appView,
LambdaRewriter rewriter,
ProgramMethod accessedFrom,
- DexType lambdaClassType,
LambdaDescriptor descriptor) {
assert rewriter != null;
- assert lambdaClassType != null;
assert descriptor != null;
-
+ this.type = builder.getType();
this.appView = appView;
this.rewriter = rewriter;
- this.type = lambdaClassType;
this.descriptor = descriptor;
- DexItemFactory factory = appView.dexItemFactory();
+ DexItemFactory factory = builder.getFactory();
DexProto constructorProto = factory.createProto(
factory.voidType, descriptor.captures.values);
- this.constructor =
- factory.createMethod(lambdaClassType, constructorProto, factory.constructorMethodName);
+ this.constructor = factory.createMethod(type, constructorProto, factory.constructorMethodName);
this.target = createTarget(accessedFrom);
@@ -109,98 +95,32 @@
this.classConstructor =
!stateless
? null
- : factory.createMethod(
- lambdaClassType, constructorProto, factory.classConstructorMethodName);
+ : factory.createMethod(type, constructorProto, factory.classConstructorMethodName);
this.lambdaField =
- !stateless
- ? null
- : factory.createField(lambdaClassType, lambdaClassType, rewriter.instanceFieldName);
+ !stateless ? null : factory.createField(type, type, rewriter.instanceFieldName);
+
+ // Synthesize the program class one all fields are set.
+ synthesizeLambdaClass(builder);
}
- // Generate unique lambda class type for lambda descriptor and instantiation point context.
- public static DexType createLambdaClassType(
- AppView<?> appView, ProgramMethod accessedFrom, LambdaDescriptor match) {
- StringBuilder lambdaClassDescriptor = new StringBuilder("L");
-
- // We always create lambda class in the same package where it is referenced.
- String packageDescriptor = accessedFrom.getHolderType().getPackageDescriptor();
- if (!packageDescriptor.isEmpty()) {
- lambdaClassDescriptor.append(packageDescriptor).append('/');
- }
-
- // Lambda class name prefix
- lambdaClassDescriptor.append(LambdaRewriter.LAMBDA_CLASS_NAME_PREFIX);
-
- // If the lambda class should match 1:1 the class it is accessed from, we
- // just add the name of this type to make lambda class name unique.
- // It also helps link the class lambda originated from in some cases.
- if (match.delegatesToLambdaImplMethod() || match.needsAccessor(accessedFrom)) {
- lambdaClassDescriptor.append(accessedFrom.getHolderType().getName()).append('$');
- }
-
- // Add unique lambda descriptor id
- lambdaClassDescriptor.append(match.uniqueId).append(';');
- return appView.dexItemFactory().createType(lambdaClassDescriptor.toString());
- }
-
- public final DexProgramClass getOrCreateLambdaClass() {
- return lazyDexClass.get();
- }
-
- private DexProgramClass synthesizeLambdaClass() {
- DexMethod mainMethod =
- appView.dexItemFactory().createMethod(type, descriptor.erasedProto, descriptor.name);
-
- DexProgramClass clazz =
- new DexProgramClass(
- type,
- null,
- new SynthesizedOrigin("lambda desugaring", getClass()),
- // Make the synthesized class public, as it might end up being accessed from a different
- // classloader (package private access is not allowed across classloaders, b/72538146).
- ClassAccessFlags.fromDexAccessFlags(
- Constants.ACC_FINAL | Constants.ACC_SYNTHETIC | Constants.ACC_PUBLIC),
- appView.dexItemFactory().objectType,
- buildInterfaces(),
- appView.dexItemFactory().createString("lambda"),
- null,
- Collections.emptyList(),
- null,
- Collections.emptyList(),
- ClassSignature.noSignature(),
- DexAnnotationSet.empty(),
- synthesizeStaticFields(),
- synthesizeInstanceFields(),
- synthesizeDirectMethods(),
- synthesizeVirtualMethods(mainMethod),
- appView.dexItemFactory().getSkipNameValidationForTesting(),
- LambdaClass::computeChecksumForSynthesizedClass);
- appView.appInfo().addSynthesizedClass(clazz, false);
-
- // The method addSynthesizedFrom() may be called concurrently. To avoid a Concurrent-
- // ModificationException we must use synchronization.
- synchronized (synthesizedFrom) {
- synthesizedFrom.forEach(clazz::addSynthesizedFrom);
- }
+ public final DexProgramClass getLambdaProgramClass() {
+ assert clazz != null;
return clazz;
}
- private static long computeChecksumForSynthesizedClass(DexProgramClass clazz) {
- // Checksum of synthesized classes are compute based off the depending input. This might
- // create false positives (ie: unchanged lambda class detected as changed even thought only
- // an unrelated part from a synthesizedFrom class is changed).
+ void setClass(DexProgramClass clazz) {
+ assert this.clazz == null;
+ assert clazz != null;
+ assert type == clazz.type;
+ this.clazz = clazz;
+ }
- // Ideally, we should use some hashcode of the dex program class that is deterministic across
- // compiles.
- Collection<DexProgramClass> synthesizedFrom = clazz.getSynthesizedFrom();
- ByteBuffer buffer = ByteBuffer.allocate(synthesizedFrom.size() * Longs.BYTES);
- for (DexProgramClass from : synthesizedFrom) {
- buffer.putLong(from.getChecksum());
- }
- CRC32 crc = new CRC32();
- byte[] array = buffer.array();
- crc.update(array, 0, array.length);
- return crc.getValue();
+ private void synthesizeLambdaClass(SyntheticProgramClassBuilder builder) {
+ builder.setInterfaces(descriptor.interfaces);
+ synthesizeStaticFields(builder);
+ synthesizeInstanceFields(builder);
+ synthesizeDirectMethods(builder);
+ synthesizeVirtualMethods(builder);
}
final DexField getCaptureField(int index) {
@@ -216,24 +136,15 @@
return descriptor.isStateless();
}
- void addSynthesizedFrom(DexProgramClass clazz) {
- assert clazz != null;
- synchronized (synthesizedFrom) {
- if (synthesizedFrom.add(clazz)) {
- // The lambda class may already have been synthesized, and we therefore need to update the
- // synthesized lambda class as well.
- getOrCreateLambdaClass().addSynthesizedFrom(clazz);
- }
- }
- }
-
// Synthesize virtual methods.
- private DexEncodedMethod[] synthesizeVirtualMethods(DexMethod mainMethod) {
- DexEncodedMethod[] methods = new DexEncodedMethod[1 + descriptor.bridges.size()];
- int index = 0;
+ private void synthesizeVirtualMethods(SyntheticProgramClassBuilder builder) {
+ DexMethod mainMethod =
+ appView.dexItemFactory().createMethod(type, descriptor.erasedProto, descriptor.name);
+
+ List<DexEncodedMethod> methods = new ArrayList<>(1 + descriptor.bridges.size());
// Synthesize main method.
- methods[index++] =
+ methods.add(
new DexEncodedMethod(
mainMethod,
MethodAccessFlags.fromSharedAccessFlags(
@@ -242,13 +153,13 @@
DexAnnotationSet.empty(),
ParameterAnnotationsList.empty(),
LambdaMainMethodSourceCode.build(this, mainMethod),
- true);
+ true));
// Synthesize bridge methods.
for (DexProto bridgeProto : descriptor.bridges) {
DexMethod bridgeMethod =
appView.dexItemFactory().createMethod(type, bridgeProto, descriptor.name);
- methods[index++] =
+ methods.add(
new DexEncodedMethod(
bridgeMethod,
MethodAccessFlags.fromSharedAccessFlags(
@@ -261,18 +172,18 @@
DexAnnotationSet.empty(),
ParameterAnnotationsList.empty(),
LambdaBridgeMethodSourceCode.build(this, bridgeMethod, mainMethod),
- true);
+ true));
}
- return methods;
+ builder.setVirtualMethods(methods);
}
// Synthesize direct methods.
- private DexEncodedMethod[] synthesizeDirectMethods() {
+ private void synthesizeDirectMethods(SyntheticProgramClassBuilder builder) {
boolean stateless = isStateless();
- DexEncodedMethod[] methods = new DexEncodedMethod[stateless ? 2 : 1];
+ List<DexEncodedMethod> methods = new ArrayList<>(stateless ? 2 : 1);
// Constructor.
- methods[0] =
+ methods.add(
new DexEncodedMethod(
constructor,
MethodAccessFlags.fromSharedAccessFlags(
@@ -283,11 +194,11 @@
DexAnnotationSet.empty(),
ParameterAnnotationsList.empty(),
LambdaConstructorSourceCode.build(this),
- true);
+ true));
// Class constructor for stateless lambda classes.
if (stateless) {
- methods[1] =
+ methods.add(
new DexEncodedMethod(
classConstructor,
MethodAccessFlags.fromSharedAccessFlags(
@@ -296,61 +207,50 @@
DexAnnotationSet.empty(),
ParameterAnnotationsList.empty(),
LambdaClassConstructorSourceCode.build(this),
- true);
- feedback.classInitializerMayBePostponed(methods[1]);
+ true));
+ feedback.classInitializerMayBePostponed(methods.get(1));
}
- return methods;
+ builder.setDirectMethods(methods);
}
// Synthesize instance fields to represent captured values.
- private DexEncodedField[] synthesizeInstanceFields() {
+ private void synthesizeInstanceFields(SyntheticProgramClassBuilder builder) {
DexType[] fieldTypes = descriptor.captures.values;
int fieldCount = fieldTypes.length;
- DexEncodedField[] fields = new DexEncodedField[fieldCount];
+ List<DexEncodedField> fields = new ArrayList<>(fieldCount);
for (int i = 0; i < fieldCount; i++) {
FieldAccessFlags accessFlags =
FieldAccessFlags.fromSharedAccessFlags(
Constants.ACC_FINAL | Constants.ACC_SYNTHETIC | Constants.ACC_PUBLIC);
- fields[i] =
+ fields.add(
new DexEncodedField(
getCaptureField(i),
accessFlags,
FieldTypeSignature.noSignature(),
DexAnnotationSet.empty(),
- null);
+ null));
}
- return fields;
+ builder.setInstanceFields(fields);
}
// Synthesize static fields to represent singleton instance.
- private DexEncodedField[] synthesizeStaticFields() {
- if (!isStateless()) {
- return DexEncodedField.EMPTY_ARRAY;
+ private void synthesizeStaticFields(SyntheticProgramClassBuilder builder) {
+ if (isStateless()) {
+ // Create instance field for stateless lambda.
+ assert this.lambdaField != null;
+ builder.setStaticFields(
+ Collections.singletonList(
+ new DexEncodedField(
+ this.lambdaField,
+ FieldAccessFlags.fromSharedAccessFlags(
+ Constants.ACC_PUBLIC
+ | Constants.ACC_FINAL
+ | Constants.ACC_SYNTHETIC
+ | Constants.ACC_STATIC),
+ FieldTypeSignature.noSignature(),
+ DexAnnotationSet.empty(),
+ DexValueNull.NULL)));
}
-
- // Create instance field for stateless lambda.
- assert this.lambdaField != null;
- DexEncodedField[] fields = new DexEncodedField[1];
- fields[0] =
- new DexEncodedField(
- this.lambdaField,
- FieldAccessFlags.fromSharedAccessFlags(
- Constants.ACC_PUBLIC
- | Constants.ACC_FINAL
- | Constants.ACC_SYNTHETIC
- | Constants.ACC_STATIC),
- FieldTypeSignature.noSignature(),
- DexAnnotationSet.empty(),
- DexValueNull.NULL);
- return fields;
- }
-
- // Build a list of implemented interfaces.
- private DexTypeList buildInterfaces() {
- List<DexType> interfaces = descriptor.interfaces;
- return interfaces.isEmpty()
- ? DexTypeList.empty()
- : new DexTypeList(interfaces.toArray(DexType.EMPTY_ARRAY));
}
// Creates a delegation target for this particular lambda class. Note that we
@@ -643,11 +543,15 @@
newMethod.getCode(), callTarget.getArity(), appView);
return newMethod;
});
-
- assert replacement != null
- : "Unexpected failure to find direct lambda target for: " + implMethod.qualifiedName();
-
- return new ProgramMethod(implMethodHolder, replacement);
+ if (replacement != null) {
+ return new ProgramMethod(implMethodHolder, replacement);
+ }
+ // The method might already have been moved by another invoke-dynamic targeting it.
+ // If so, it must be defined on the holder.
+ ProgramMethod modified = implMethodHolder.lookupProgramMethod(callTarget);
+ assert modified != null;
+ assert modified.getDefinition().isNonPrivateVirtualMethod();
+ return modified;
}
}
@@ -713,11 +617,27 @@
rewriter.forcefullyMoveMethod(encodedMethod.method, callTarget);
return newMethod;
});
- return new ProgramMethod(implMethodHolder, replacement);
+ if (replacement != null) {
+ return new ProgramMethod(implMethodHolder, replacement);
+ }
+ // The method might already have been moved by another invoke-dynamic targeting it.
+ // If so, it must be defined on the holder.
+ ProgramMethod modified = implMethodHolder.lookupProgramMethod(callTarget);
+ assert modified != null;
+ assert modified.getDefinition().isNonPrivateVirtualMethod();
+ return modified;
}
private ProgramMethod createSyntheticAccessor(
DexMethod implMethod, DexProgramClass implMethodHolder) {
+ // The accessor might already have been created by another invoke-dynamic targeting it.
+ ProgramMethod existing = implMethodHolder.lookupProgramMethod(callTarget);
+ if (existing != null) {
+ assert existing.getAccessFlags().isSynthetic();
+ assert existing.getAccessFlags().isPublic();
+ assert existing.getDefinition().isVirtualMethod();
+ return existing;
+ }
MethodAccessFlags accessorFlags =
MethodAccessFlags.fromSharedAccessFlags(
Constants.ACC_SYNTHETIC | Constants.ACC_PUBLIC, false);
@@ -762,6 +682,15 @@
DexProgramClass accessorClass = appView.definitionForProgramType(callTarget.holder);
assert accessorClass != null;
+ // The accessor might already have been created by another invoke-dynamic targeting it.
+ ProgramMethod existing = accessorClass.lookupProgramMethod(callTarget);
+ if (existing != null) {
+ assert existing.getAccessFlags().isSynthetic();
+ assert existing.getAccessFlags().isPublic();
+ assert existing.getAccessFlags().isStatic();
+ return existing;
+ }
+
// Always make the method public to provide access when r8 minification is allowed to move
// the lambda class accessing this method to another package (-allowaccessmodification).
MethodAccessFlags accessorFlags =
@@ -779,11 +708,7 @@
AccessorMethodSourceCode.build(LambdaClass.this, callTarget),
true);
- // We may arrive here concurrently so we need must update the methods of the class atomically.
- synchronized (accessorClass) {
- accessorClass.addDirectMethod(accessorEncodedMethod);
- }
-
+ accessorClass.addDirectMethod(accessorEncodedMethod);
return new ProgramMethod(accessorClass, accessorEncodedMethod);
}
}
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 f464fdd..165cb52 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
@@ -16,7 +16,6 @@
import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.CfCode;
-import com.android.tools.r8.graph.DexApplication.Builder;
import com.android.tools.r8.graph.DexCallSite;
import com.android.tools.r8.graph.DexField;
import com.android.tools.r8.graph.DexItemFactory;
@@ -28,32 +27,25 @@
import com.android.tools.r8.graph.GraphLens;
import com.android.tools.r8.graph.GraphLens.NestedGraphLens;
import com.android.tools.r8.graph.ProgramMethod;
-import com.android.tools.r8.ir.analysis.type.Nullability;
-import com.android.tools.r8.ir.analysis.type.TypeElement;
-import com.android.tools.r8.ir.code.BasicBlock;
-import com.android.tools.r8.ir.code.IRCode;
-import com.android.tools.r8.ir.code.InstructionListIterator;
-import com.android.tools.r8.ir.code.InvokeCustom;
-import com.android.tools.r8.ir.code.InvokeDirect;
-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.code.ValueType;
import com.android.tools.r8.ir.conversion.IRConverter;
-import com.android.tools.r8.utils.DescriptorUtils;
+import com.android.tools.r8.synthesis.SyntheticNaming;
+import com.android.tools.r8.utils.Box;
import com.android.tools.r8.utils.collections.BidirectionalManyToOneRepresentativeMap;
import com.android.tools.r8.utils.collections.BidirectionalOneToOneMap;
import com.android.tools.r8.utils.collections.SortedProgramMethodSet;
import com.google.common.base.Suppliers;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Sets;
+import com.google.common.hash.Hasher;
+import com.google.common.hash.Hashing;
import java.util.ArrayList;
import java.util.Collection;
-import java.util.IdentityHashMap;
+import java.util.Collections;
import java.util.List;
-import java.util.ListIterator;
import java.util.Map;
import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.function.Function;
@@ -69,7 +61,6 @@
public class LambdaRewriter {
// Public for testing.
- public static final String LAMBDA_CLASS_NAME_PREFIX = "-$$Lambda$";
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";
@@ -81,16 +72,11 @@
private final LambdaRewriterLens.Builder lensBuilder = LambdaRewriterLens.builder();
private final Set<DexMethod> forcefullyMovedMethods = Sets.newIdentityHashSet();
- // Maps call sites seen so far to inferred lambda descriptor. It is intended
- // to help avoid re-matching call sites we already seen. Note that same call
- // site may match one or several lambda classes.
- //
- // NOTE: synchronize concurrent access on `knownCallSites`.
- private final Map<DexCallSite, LambdaDescriptor> knownCallSites = new IdentityHashMap<>();
- // Maps lambda class type into lambda class representation. Since lambda class
- // type uniquely defines lambda class, effectively canonicalizes lambda classes.
+ // Maps lambda class type into lambda class representation.
// NOTE: synchronize concurrent access on `knownLambdaClasses`.
- private final Map<DexType, LambdaClass> knownLambdaClasses = new IdentityHashMap<>();
+ private final List<LambdaClass> knownLambdaClasses = new ArrayList<>();
+
+ private final Map<DexMethod, Integer> methodIds = new ConcurrentHashMap<>();
public LambdaRewriter(AppView<?> appView) {
this.appView = appView;
@@ -138,7 +124,7 @@
if (descriptor == null) {
return null;
}
- return getOrCreateLambdaClass(descriptor, method);
+ return createLambdaClass(descriptor, method);
});
}
@@ -209,16 +195,10 @@
}
/** Generates lambda classes and adds them to the builder. */
- public void finalizeLambdaDesugaringForD8(
- Builder<?> builder, IRConverter converter, ExecutorService executorService)
+ public void finalizeLambdaDesugaringForD8(IRConverter converter, ExecutorService executorService)
throws ExecutionException {
synthesizeAccessibilityBridgesForLambdaClassesD8(
- knownLambdaClasses.values(), converter, executorService);
- for (LambdaClass lambdaClass : knownLambdaClasses.values()) {
- DexProgramClass synthesizedClass = lambdaClass.getOrCreateLambdaClass();
- appView.appInfo().addSynthesizedClass(synthesizedClass, lambdaClass.addToMainDexList.get());
- builder.addSynthesizedClass(synthesizedClass);
- }
+ knownLambdaClasses, converter, executorService);
fixup();
optimizeSynthesizedClasses(converter, executorService);
}
@@ -226,183 +206,44 @@
private void optimizeSynthesizedClasses(IRConverter converter, ExecutorService executorService)
throws ExecutionException {
converter.optimizeSynthesizedClasses(
- knownLambdaClasses.values().stream()
- .map(LambdaClass::getOrCreateLambdaClass)
+ knownLambdaClasses.stream()
+ .map(LambdaClass::getLambdaProgramClass)
.collect(ImmutableSet.toImmutableSet()),
executorService);
}
- // Matches invoke-custom instruction operands to infer lambda descriptor
- // corresponding to this lambda invocation point.
- //
- // Returns the lambda descriptor or `MATCH_FAILED`.
- private LambdaDescriptor inferLambdaDescriptor(DexCallSite callSite, ProgramMethod context) {
- // We check the map before and after inferring lambda descriptor to minimize time
- // spent in synchronized block. As a result we may throw away calculated descriptor
- // in rare case when another thread has same call site processed concurrently,
- // but this is a low price to pay comparing to making whole method synchronous.
- LambdaDescriptor descriptor = getKnown(knownCallSites, callSite);
- return descriptor != null
- ? descriptor
- : putIfAbsent(
- knownCallSites,
- callSite,
- LambdaDescriptor.infer(callSite, appView.appInfoForDesugaring(), context));
- }
-
- // Returns a lambda class corresponding to the lambda descriptor and context,
- // creates the class if it does not yet exist.
- public LambdaClass getOrCreateLambdaClass(
- LambdaDescriptor descriptor, ProgramMethod accessedFrom) {
- DexType lambdaClassType = LambdaClass.createLambdaClassType(appView, accessedFrom, descriptor);
- // We check the map twice to to minimize time spent in synchronized block.
- LambdaClass lambdaClass = getKnown(knownLambdaClasses, lambdaClassType);
- if (lambdaClass == null) {
- lambdaClass =
- putIfAbsent(
- knownLambdaClasses,
- lambdaClassType,
- new LambdaClass(appView, this, accessedFrom, lambdaClassType, descriptor));
- if (appView.options().isDesugaredLibraryCompilation()) {
- DexType rewrittenType =
- appView.rewritePrefix.rewrittenType(accessedFrom.getHolderType(), appView);
- if (rewrittenType == null) {
- rewrittenType =
- appView
- .options()
- .desugaredLibraryConfiguration
- .getEmulateLibraryInterface()
- .get(accessedFrom.getHolderType());
- }
- if (rewrittenType != null) {
- addRewritingPrefix(accessedFrom, rewrittenType, lambdaClassType);
- }
- }
- }
- lambdaClass.addSynthesizedFrom(accessedFrom.getHolder());
- if (appView.appInfo().getMainDexClasses().contains(accessedFrom.getHolder())) {
- lambdaClass.addToMainDexList.set(true);
+ // Creates a lambda class corresponding to the lambda descriptor and context.
+ public LambdaClass createLambdaClass(LambdaDescriptor descriptor, ProgramMethod accessedFrom) {
+ int nextId =
+ methodIds.compute(
+ accessedFrom.getReference(), (method, value) -> value == null ? 0 : value + 1);
+ Box<LambdaClass> box = new Box<>();
+ DexProgramClass clazz =
+ appView
+ .getSyntheticItems()
+ .createClass(
+ SyntheticNaming.SyntheticKind.LAMBDA,
+ accessedFrom.getHolder(),
+ appView.dexItemFactory(),
+ // TODO(b/172194101): Make this part of a unique context construction.
+ () -> {
+ Hasher hasher = Hashing.sha256().newHasher();
+ accessedFrom.getReference().hash(hasher);
+ return "$" + hasher.hash().toString() + "$" + nextId;
+ },
+ builder ->
+ box.set(new LambdaClass(builder, appView, this, accessedFrom, descriptor)));
+ // Immediately set the actual program class on the lambda.
+ LambdaClass lambdaClass = box.get();
+ lambdaClass.setClass(clazz);
+ synchronized (knownLambdaClasses) {
+ knownLambdaClasses.add(lambdaClass);
}
return lambdaClass;
}
- private LambdaClass getKnownLambdaClass(LambdaDescriptor descriptor, ProgramMethod accessedFrom) {
- DexType lambdaClassType = LambdaClass.createLambdaClassType(appView, accessedFrom, descriptor);
- return getKnown(knownLambdaClasses, lambdaClassType);
- }
-
- private void addRewritingPrefix(
- ProgramMethod context, DexType rewritten, DexType lambdaClassType) {
- String javaName = lambdaClassType.toString();
- String typeString = context.getHolderType().toString();
- String actualPrefix = typeString.substring(0, typeString.lastIndexOf('.'));
- String rewrittenString = rewritten.toString();
- String actualRewrittenPrefix = rewrittenString.substring(0, rewrittenString.lastIndexOf('.'));
- assert javaName.startsWith(actualPrefix);
- appView.rewritePrefix.rewriteType(
- lambdaClassType,
- appView
- .dexItemFactory()
- .createType(
- DescriptorUtils.javaTypeToDescriptor(
- actualRewrittenPrefix + javaName.substring(actualPrefix.length()))));
- }
-
- private static <K, V> V getKnown(Map<K, V> map, K key) {
- synchronized (map) {
- return map.get(key);
- }
- }
-
- private static <K, V> V putIfAbsent(Map<K, V> map, K key, V value) {
- synchronized (map) {
- V known = map.get(key);
- if (known != null) {
- return known;
- }
- map.put(key, value);
- return value;
- }
- }
-
- // Patches invoke-custom instruction to create or get an instance
- // of the generated lambda class.
- private void patchInstruction(
- InvokeCustom invoke,
- LambdaClass lambdaClass,
- IRCode code,
- ListIterator<BasicBlock> blocks,
- InstructionListIterator instructions,
- Set<Value> affectedValues) {
- assert lambdaClass != null;
- assert instructions != null;
-
- // The value representing new lambda instance: we reuse the
- // value from the original invoke-custom instruction, and thus
- // all its usages.
- Value lambdaInstanceValue = invoke.outValue();
- if (lambdaInstanceValue == null) {
- // The out value might be empty in case it was optimized out.
- lambdaInstanceValue =
- code.createValue(
- TypeElement.fromDexType(lambdaClass.type, Nullability.maybeNull(), appView));
- } else {
- affectedValues.add(lambdaInstanceValue);
- }
-
- // For stateless lambdas we replace InvokeCustom instruction with StaticGet
- // reading the value of INSTANCE field created for singleton lambda class.
- if (lambdaClass.isStateless()) {
- instructions.replaceCurrentInstruction(
- new StaticGet(lambdaInstanceValue, lambdaClass.lambdaField));
- // Note that since we replace one throwing operation with another we don't need
- // to have any special handling for catch handlers.
- return;
- }
-
- // For stateful lambdas we always create a new instance since we need to pass
- // captured values to the constructor.
- //
- // We replace InvokeCustom instruction with a new NewInstance instruction
- // instantiating lambda followed by InvokeDirect instruction calling a
- // constructor on it.
- //
- // original:
- // Invoke-Custom rResult <- { rArg0, rArg1, ... }; call site: ...
- //
- // result:
- // NewInstance rResult <- LambdaClass
- // Invoke-Direct { rResult, rArg0, rArg1, ... }; method: void LambdaClass.<init>(...)
- lambdaInstanceValue.setType(
- lambdaInstanceValue.getType().asReferenceType().asDefinitelyNotNull());
- NewInstance newInstance = new NewInstance(lambdaClass.type, lambdaInstanceValue);
- instructions.replaceCurrentInstruction(newInstance);
-
- List<Value> arguments = new ArrayList<>();
- arguments.add(lambdaInstanceValue);
- arguments.addAll(invoke.arguments()); // Optional captures.
- InvokeDirect constructorCall =
- new InvokeDirect(lambdaClass.constructor, null /* no return value */, arguments);
- instructions.add(constructorCall);
- constructorCall.setPosition(newInstance.getPosition());
-
- // If we don't have catch handlers we are done.
- if (!constructorCall.getBlock().hasCatchHandlers()) {
- return;
- }
-
- // Move the iterator back to position it between the two instructions, split
- // the block between the two instructions, and copy the catch handlers.
- instructions.previous();
- assert instructions.peekNext().isInvokeDirect();
- BasicBlock currentBlock = newInstance.getBlock();
- BasicBlock nextBlock = instructions.split(code, blocks);
- assert !instructions.hasNext();
- nextBlock.copyCatchHandlers(code, blocks, currentBlock, appView.options());
- }
-
- public Map<DexType, LambdaClass> getKnownLambdaClasses() {
- return knownLambdaClasses;
+ public Collection<LambdaClass> getKnownLambdaClasses() {
+ return Collections.unmodifiableList(knownLambdaClasses);
}
public NestedGraphLens fixup() {
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/NestBasedAccessDesugaring.java b/src/main/java/com/android/tools/r8/ir/desugar/NestBasedAccessDesugaring.java
deleted file mode 100644
index eb42231..0000000
--- a/src/main/java/com/android/tools/r8/ir/desugar/NestBasedAccessDesugaring.java
+++ /dev/null
@@ -1,529 +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.ir.desugar;
-
-
-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.ClassAccessFlags;
-import com.android.tools.r8.graph.ClasspathMethod;
-import com.android.tools.r8.graph.DexAnnotationSet;
-import com.android.tools.r8.graph.DexApplication;
-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.DexField;
-import com.android.tools.r8.graph.DexMethod;
-import com.android.tools.r8.graph.DexProgramClass;
-import com.android.tools.r8.graph.DexProto;
-import com.android.tools.r8.graph.DexString;
-import com.android.tools.r8.graph.DexType;
-import com.android.tools.r8.graph.DexTypeList;
-import com.android.tools.r8.graph.GenericSignature.ClassSignature;
-import com.android.tools.r8.graph.NestMemberClassAttribute;
-import com.android.tools.r8.graph.ProgramMethod;
-import com.android.tools.r8.graph.UseRegistry;
-import com.android.tools.r8.ir.code.Invoke;
-import com.android.tools.r8.origin.SynthesizedOrigin;
-import com.android.tools.r8.utils.BooleanUtils;
-import com.android.tools.r8.utils.Pair;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-import java.util.Map;
-import java.util.Objects;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Future;
-
-// NestBasedAccessDesugaring contains common code between the two subclasses
-// which are specialized for d8 and r8
-public abstract class NestBasedAccessDesugaring {
-
- // Short names to avoid creating long strings
- public static final String NEST_ACCESS_NAME_PREFIX = "-$$Nest$";
- private static final String NEST_ACCESS_METHOD_NAME_PREFIX = NEST_ACCESS_NAME_PREFIX + "m";
- private static final String NEST_ACCESS_STATIC_METHOD_NAME_PREFIX =
- NEST_ACCESS_NAME_PREFIX + "sm";
- private static final String NEST_ACCESS_FIELD_GET_NAME_PREFIX = NEST_ACCESS_NAME_PREFIX + "fget";
- private static final String NEST_ACCESS_STATIC_GET_FIELD_NAME_PREFIX =
- NEST_ACCESS_NAME_PREFIX + "sfget";
- 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;
- // Following maps are there to avoid creating the bridges multiple times
- // and remember the bridges to add once the nests are processed.
- final Map<DexMethod, ProgramMethod> bridges = new ConcurrentHashMap<>();
- final Map<DexField, ProgramMethod> getFieldBridges = new ConcurrentHashMap<>();
- final Map<DexField, ProgramMethod> putFieldBridges = new ConcurrentHashMap<>();
- // Common single empty class for nest based private constructors
- private final DexProgramClass nestConstructor;
- private boolean nestConstructorUsed = false;
-
- NestBasedAccessDesugaring(AppView<?> appView) {
- this.appView = appView;
- this.nestConstructor = createNestAccessConstructor();
- }
-
- DexType getNestConstructorType() {
- assert nestConstructor != null;
- return nestConstructor.type;
- }
-
- abstract void reportMissingNestHost(DexClass clazz);
-
- abstract void reportIncompleteNest(List<DexType> nest);
-
- DexClass definitionFor(DexType type) {
- return appView.definitionFor(appView.graphLens().lookupType(type));
- }
-
- private DexEncodedMethod lookupOnHolder(
- DexMethod method, DexClassAndMethod context, Invoke.Type invokeType) {
- DexMethod rewritten =
- appView.graphLens().lookupMethod(method, context.getReference(), invokeType).getReference();
- return rewritten.lookupOnClass(appView.definitionForHolder(rewritten));
- }
-
- private DexEncodedField lookupOnHolder(DexField field) {
- DexField rewritten = appView.graphLens().lookupField(field);
- return rewritten.lookupOnClass(appView.definitionForHolder(rewritten));
- }
-
- // Extract the list of types in the programClass' nest, of host hostClass
- private Pair<DexClass, List<DexType>> extractNest(DexClass clazz) {
- assert clazz != null;
- DexClass hostClass = clazz.isNestHost() ? clazz : definitionFor(clazz.getNestHost());
- if (hostClass == null) {
- reportMissingNestHost(clazz);
- // Missing nest host means the class is considered as not being part of a nest.
- clazz.clearNestHost();
- return null;
- }
- List<DexType> classesInNest =
- new ArrayList<>(hostClass.getNestMembersClassAttributes().size() + 1);
- for (NestMemberClassAttribute nestmate : hostClass.getNestMembersClassAttributes()) {
- classesInNest.add(nestmate.getNestMember());
- }
- classesInNest.add(hostClass.type);
- return new Pair<>(hostClass, classesInNest);
- }
-
- Future<?> asyncProcessNest(DexProgramClass clazz, ExecutorService executorService) {
- return executorService.submit(
- () -> {
- Pair<DexClass, List<DexType>> nest = extractNest(clazz);
- // Nest is null when nest host is missing, we do nothing in this case.
- if (nest != null) {
- processNest(nest.getFirst(), nest.getSecond());
- }
- return null; // we want a Callable not a Runnable to be able to throw
- });
- }
-
- private void processNest(DexClass host, List<DexType> nest) {
- boolean reported = false;
- for (DexType type : nest) {
- DexClass clazz = definitionFor(type);
- if (clazz == null) {
- if (!reported) {
- reportIncompleteNest(nest);
- reported = true;
- }
- } else {
- reportDesugarDependencies(host, clazz);
- if (shouldProcessClassInNest(clazz, nest)) {
- for (DexEncodedMethod definition : clazz.methods()) {
- if (clazz.isProgramClass()) {
- ProgramMethod method = new ProgramMethod(clazz.asProgramClass(), definition);
- method.registerCodeReferences(new NestBasedAccessDesugaringUseRegistry(method));
- } else if (clazz.isClasspathClass()) {
- ClasspathMethod method = new ClasspathMethod(clazz.asClasspathClass(), definition);
- method.registerCodeReferencesForDesugaring(
- new NestBasedAccessDesugaringUseRegistry(method));
- } else {
- assert false;
- }
- }
- }
- }
- }
- }
-
- private void reportDesugarDependencies(DexClass host, DexClass clazz) {
- if (host == clazz) {
- return;
- }
- if (host.isProgramClass()) {
- InterfaceMethodRewriter.reportDependencyEdge(host.asProgramClass(), clazz, appView.options());
- }
- if (clazz.isProgramClass()) {
- InterfaceMethodRewriter.reportDependencyEdge(clazz.asProgramClass(), host, appView.options());
- }
- }
-
- protected abstract boolean shouldProcessClassInNest(DexClass clazz, List<DexType> nest);
-
- private DexProgramClass createNestAccessConstructor() {
- // TODO(b/176900254): The class generated should be in a package that has lower change of
- // collisions (and perhaps be unique by some hash from the nest).
- return new DexProgramClass(
- appView.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),
- appView.dexItemFactory().objectType,
- DexTypeList.empty(),
- appView.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,
- appView.dexItemFactory().getSkipNameValidationForTesting(),
- DexProgramClass::checksumFromType);
- }
-
- void synthesizeNestConstructor(DexApplication.Builder<?> builder) {
- if (nestConstructorUsed) {
- appView.appInfo().addSynthesizedClass(nestConstructor, true);
- builder.addSynthesizedClass(nestConstructor);
- }
- }
-
- private DexString computeMethodBridgeName(DexEncodedMethod method) {
- String methodName = method.method.name.toString();
- String fullName;
- if (method.isStatic()) {
- fullName = NEST_ACCESS_STATIC_METHOD_NAME_PREFIX + methodName;
- } else {
- fullName = NEST_ACCESS_METHOD_NAME_PREFIX + methodName;
- }
- return appView.dexItemFactory().createString(fullName);
- }
-
- private DexString computeFieldBridgeName(DexEncodedField field, boolean isGet) {
- String fieldName = field.field.name.toString();
- String fullName;
- if (isGet && !field.isStatic()) {
- fullName = NEST_ACCESS_FIELD_GET_NAME_PREFIX + fieldName;
- } else if (isGet) {
- fullName = NEST_ACCESS_STATIC_GET_FIELD_NAME_PREFIX + fieldName;
- } else if (!field.isStatic()) {
- fullName = NEST_ACCESS_FIELD_PUT_NAME_PREFIX + fieldName;
- } else {
- fullName = NEST_ACCESS_STATIC_PUT_FIELD_NAME_PREFIX + fieldName;
- }
- return appView.dexItemFactory().createString(fullName);
- }
-
- private DexMethod computeMethodBridge(DexEncodedMethod encodedMethod) {
- DexMethod method = encodedMethod.method;
- DexProto proto =
- encodedMethod.accessFlags.isStatic()
- ? method.proto
- : appView.dexItemFactory().prependHolderToProto(method);
- return appView
- .dexItemFactory()
- .createMethod(method.holder, proto, computeMethodBridgeName(encodedMethod));
- }
-
- private DexMethod computeInitializerBridge(DexMethod method) {
- DexProto newProto =
- appView.dexItemFactory().appendTypeToProto(method.proto, nestConstructor.type);
- return appView.dexItemFactory().createMethod(method.holder, newProto, method.name);
- }
-
- private DexMethod computeFieldBridge(DexEncodedField field, boolean isGet) {
- DexType holderType = field.getHolderType();
- DexType fieldType = field.field.type;
- int bridgeParameterCount =
- BooleanUtils.intValue(!field.isStatic()) + BooleanUtils.intValue(!isGet);
- DexType[] parameters = new DexType[bridgeParameterCount];
- if (!isGet) {
- parameters[parameters.length - 1] = fieldType;
- }
- if (!field.isStatic()) {
- parameters[0] = holderType;
- }
- DexType returnType = isGet ? fieldType : appView.dexItemFactory().voidType;
- DexProto proto = appView.dexItemFactory().createProto(returnType, parameters);
- return appView
- .dexItemFactory()
- .createMethod(holderType, proto, computeFieldBridgeName(field, isGet));
- }
-
- boolean invokeRequiresRewriting(DexEncodedMethod method, DexClassAndMethod context) {
- assert method != null;
- // Rewrite only when targeting other nest members private fields.
- if (!method.accessFlags.isPrivate() || method.getHolderType() == context.getHolderType()) {
- return false;
- }
- DexClass methodHolder = definitionFor(method.getHolderType());
- assert methodHolder != null; // from encodedMethod
- return methodHolder.getNestHost() == context.getHolder().getNestHost();
- }
-
- boolean fieldAccessRequiresRewriting(DexEncodedField field, DexClassAndMethod context) {
- assert field != null;
- // Rewrite only when targeting other nest members private fields.
- if (!field.accessFlags.isPrivate() || field.getHolderType() == context.getHolderType()) {
- return false;
- }
- DexClass fieldHolder = definitionFor(field.getHolderType());
- assert fieldHolder != null; // from encodedField
- return fieldHolder.getNestHost() == context.getHolder().getNestHost();
- }
-
- private boolean holderRequiresBridge(DexClass holder) {
- // Bridges are added on program classes only.
- // Bridges on class paths are added in different compilation units.
- if (holder.isProgramClass()) {
- return false;
- } else if (holder.isClasspathClass()) {
- return true;
- }
- assert holder.isLibraryClass();
- Pair<DexClass, List<DexType>> nest = extractNest(holder);
- assert nest != null : "Should be a compilation error if missing nest host on library class.";
- reportIncompleteNest(nest.getSecond());
- throw new Unreachable(
- "Incomplete nest due to missing library class should raise a compilation error.");
- }
-
- DexMethod ensureFieldAccessBridge(DexEncodedField field, boolean isGet) {
- DexClass holder = definitionFor(field.getHolderType());
- assert holder != null;
- DexMethod bridgeMethod = computeFieldBridge(field, isGet);
- if (holderRequiresBridge(holder)) {
- return bridgeMethod;
- }
- assert holder.isProgramClass();
- // The map is used to avoid creating multiple times the bridge
- // and remembers the bridges to add.
- Map<DexField, ProgramMethod> fieldMap = isGet ? getFieldBridges : putFieldBridges;
- assert holder.isProgramClass();
- fieldMap.computeIfAbsent(
- field.field,
- k ->
- DexEncodedMethod.createFieldAccessorBridge(
- new DexFieldWithAccess(field, isGet), holder.asProgramClass(), bridgeMethod));
- return bridgeMethod;
- }
-
- DexMethod ensureInvokeBridge(DexEncodedMethod method) {
- // We add bridges only when targeting other nest members.
- DexClass holder = definitionFor(method.getHolderType());
- assert holder != null;
- DexMethod bridgeMethod;
- if (method.isInstanceInitializer()) {
- nestConstructorUsed = true;
- bridgeMethod = computeInitializerBridge(method.method);
- } else {
- bridgeMethod = computeMethodBridge(method);
- }
- if (holderRequiresBridge(holder)) {
- return bridgeMethod;
- }
- // The map is used to avoid creating multiple times the bridge
- // and remembers the bridges to add.
- assert holder.isProgramClass();
- bridges.computeIfAbsent(
- method.method,
- k ->
- method.isInstanceInitializer()
- ? method.toInitializerForwardingBridge(holder.asProgramClass(), bridgeMethod)
- : method.toStaticForwardingBridge(
- holder.asProgramClass(), computeMethodBridge(method)));
- return bridgeMethod;
- }
-
- protected class NestBasedAccessDesugaringUseRegistry extends UseRegistry {
-
- private final DexClassAndMethod context;
-
- NestBasedAccessDesugaringUseRegistry(DexClassAndMethod context) {
- super(appView.options().itemFactory);
- this.context = context;
- }
-
- private void registerInvoke(DexMethod method, Invoke.Type invokeType) {
- // Calls to non class type are not done through nest based access control.
- // Work-around for calls to enum.clone().
- if (!method.holder.isClassType()) {
- return;
- }
- DexEncodedMethod encodedMethod = lookupOnHolder(method, context, invokeType);
- if (encodedMethod != null && invokeRequiresRewriting(encodedMethod, context)) {
- ensureInvokeBridge(encodedMethod);
- }
- }
-
- private void registerFieldAccess(DexField field, boolean isGet) {
- // Since we only need to desugar accesses to private fields, and all accesses to private
- // fields must be accessing the private field directly on its holder, we can lookup the field
- // on the holder instead of resolving the field.
- DexEncodedField encodedField = lookupOnHolder(field);
- if (encodedField != null && fieldAccessRequiresRewriting(encodedField, context)) {
- ensureFieldAccessBridge(encodedField, isGet);
- }
- }
-
- @Override
- public void registerInitClass(DexType clazz) {
- // Nothing to do since we always use a public field for initializing the class.
- }
-
- @Override
- public void registerInvokeVirtual(DexMethod method) {
- // Calls to class nest mate private methods are targeted by invokeVirtual in jdk11.
- // The spec recommends to do so, but do not enforce it, hence invokeDirect is also registered.
- registerInvoke(method, Invoke.Type.VIRTUAL);
- }
-
- @Override
- public void registerInvokeDirect(DexMethod method) {
- registerInvoke(method, Invoke.Type.DIRECT);
- }
-
- @Override
- public void registerInvokeStatic(DexMethod method) {
- registerInvoke(method, Invoke.Type.STATIC);
- }
-
- @Override
- public void registerInvokeInterface(DexMethod method) {
- // Calls to interface nest mate private methods are targeted by invokeInterface in jdk11.
- // The spec recommends to do so, but do not enforce it, hence invokeDirect is also registered.
- registerInvoke(method, Invoke.Type.INTERFACE);
- }
-
- @Override
- public void registerInvokeSuper(DexMethod method) {
- registerInvoke(method, Invoke.Type.SUPER);
- }
-
- @Override
- public void registerInstanceFieldWrite(DexField field) {
- registerFieldAccess(field, false);
- }
-
- @Override
- public void registerInstanceFieldRead(DexField field) {
- registerFieldAccess(field, true);
- }
-
- @Override
- public void registerNewInstance(DexType type) {
- // Unrelated to access based control.
- // The <init> method has to be rewritten instead
- // and <init> is called through registerInvoke.
- }
-
- @Override
- public void registerStaticFieldRead(DexField field) {
- registerFieldAccess(field, true);
- }
-
- @Override
- public void registerStaticFieldWrite(DexField field) {
- registerFieldAccess(field, false);
- }
-
- @Override
- public void registerTypeReference(DexType type) {
- // Unrelated to access based control.
- }
-
- @Override
- public void registerInstanceOf(DexType type) {
- // Unrelated to access based control.
- }
- }
-
- public static final class DexFieldWithAccess {
-
- private final DexEncodedField field;
- private final boolean isGet;
-
- DexFieldWithAccess(DexEncodedField field, boolean isGet) {
- this.field = field;
- this.isGet = isGet;
- }
-
- @Override
- public int hashCode() {
- return Objects.hash(field, isGet);
- }
-
- @Override
- public boolean equals(Object o) {
- if (o == null) {
- return false;
- }
- if (getClass() != o.getClass()) {
- return false;
- }
- DexFieldWithAccess other = (DexFieldWithAccess) o;
- return isGet == other.isGet && field == other.field;
- }
-
- public boolean isGet() {
- return isGet;
- }
-
- public boolean isStatic() {
- return field.accessFlags.isStatic();
- }
-
- public boolean isPut() {
- return !isGet();
- }
-
- public boolean isInstance() {
- return !isStatic();
- }
-
- public boolean isStaticGet() {
- return isStatic() && isGet();
- }
-
- public boolean isStaticPut() {
- return isStatic() && isPut();
- }
-
- public boolean isInstanceGet() {
- return isInstance() && isGet();
- }
-
- public boolean isInstancePut() {
- return isInstance() && isPut();
- }
-
- public DexType getType() {
- return field.field.type;
- }
-
- public DexType getHolder() {
- return field.getHolderType();
- }
-
- public DexField getField() {
- return field.field;
- }
- }
-}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/NestedPrivateMethodLens.java b/src/main/java/com/android/tools/r8/ir/desugar/NestedPrivateMethodLens.java
deleted file mode 100644
index 12418b3..0000000
--- a/src/main/java/com/android/tools/r8/ir/desugar/NestedPrivateMethodLens.java
+++ /dev/null
@@ -1,161 +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.ir.desugar;
-
-import com.android.tools.r8.graph.AppView;
-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.graph.GraphLens;
-import com.android.tools.r8.graph.GraphLens.NestedGraphLens;
-import com.android.tools.r8.graph.RewrittenPrototypeDescription;
-import com.android.tools.r8.ir.code.Invoke.Type;
-import com.android.tools.r8.utils.collections.EmptyBidirectionalOneToOneMap;
-import com.google.common.collect.ImmutableMap;
-import java.util.IdentityHashMap;
-import java.util.Map;
-
-public class NestedPrivateMethodLens extends NestedGraphLens {
-
- // Map from nestHost to nest members including nest hosts
- private final DexType nestConstructorType;
- private final Map<DexField, DexMethod> getFieldMap;
- private final Map<DexField, DexMethod> putFieldMap;
-
- NestedPrivateMethodLens(
- AppView<?> appView,
- DexType nestConstructorType,
- Map<DexMethod, DexMethod> methodMap,
- Map<DexField, DexMethod> getFieldMap,
- Map<DexField, DexMethod> putFieldMap,
- GraphLens previousLens) {
- super(
- ImmutableMap.of(),
- methodMap,
- new EmptyBidirectionalOneToOneMap<>(),
- new EmptyBidirectionalOneToOneMap<>(),
- previousLens,
- appView.dexItemFactory());
- // No concurrent maps here, we do not want synchronization overhead.
- assert methodMap instanceof IdentityHashMap;
- assert getFieldMap instanceof IdentityHashMap;
- assert putFieldMap instanceof IdentityHashMap;
- this.nestConstructorType = nestConstructorType;
- this.getFieldMap = getFieldMap;
- this.putFieldMap = putFieldMap;
- }
-
- private DexMethod lookupFieldForMethod(
- DexField field, DexMethod context, Map<DexField, DexMethod> map) {
- assert context != null;
- DexMethod bridge = map.get(field);
- if (bridge != null && bridge.holder != context.holder) {
- return bridge;
- }
- return null;
- }
-
- @Override
- public DexMethod lookupGetFieldForMethod(DexField field, DexMethod context) {
- assert getPrevious().lookupGetFieldForMethod(field, context) == null;
- return lookupFieldForMethod(field, context, getFieldMap);
- }
-
- @Override
- public DexMethod lookupPutFieldForMethod(DexField field, DexMethod context) {
- assert getPrevious().lookupPutFieldForMethod(field, context) == null;
- return lookupFieldForMethod(field, context, putFieldMap);
- }
-
- @Override
- public boolean isContextFreeForMethods() {
- return methodMap.isEmpty();
- }
-
- @Override
- public boolean verifyIsContextFreeForMethod(DexMethod method) {
- assert !methodMap.containsKey(getPrevious().lookupMethod(method));
- return true;
- }
-
- // This is true because mappings specific to this class can be filled.
- @Override
- protected boolean isLegitimateToHaveEmptyMappings() {
- return true;
- }
-
- private boolean isConstructorBridge(DexMethod method) {
- DexType[] parameters = method.proto.parameters.values;
- if (parameters.length == 0) {
- return false;
- }
- DexType lastParameterType = parameters[parameters.length - 1];
- return lastParameterType == nestConstructorType;
- }
-
- @Override
- protected RewrittenPrototypeDescription internalDescribePrototypeChanges(
- RewrittenPrototypeDescription prototypeChanges, DexMethod method) {
- if (isConstructorBridge(method)) {
- // TODO (b/132767654): Try to write a test which breaks that assertion.
- assert prototypeChanges.isEmpty();
- // TODO(b/164901008): Fix when the number of arguments overflows.
- return RewrittenPrototypeDescription.none().withExtraUnusedNullParameter();
- }
- return prototypeChanges;
- }
-
- @Override
- public MethodLookupResult internalDescribeLookupMethod(
- MethodLookupResult previous, DexMethod context) {
- DexMethod bridge = methodMap.get(previous.getReference());
- if (bridge == null) {
- return previous;
- }
- assert context != null : "Guaranteed by isContextFreeForMethod";
- if (bridge.holder == context.holder) {
- return previous;
- }
- MethodLookupResult.Builder resultBuilder =
- MethodLookupResult.builder(this).setReference(bridge);
- if (isConstructorBridge(bridge)) {
- resultBuilder
- .setPrototypeChanges(
- internalDescribePrototypeChanges(previous.getPrototypeChanges(), bridge))
- .setType(Type.DIRECT);
- } else {
- resultBuilder.setPrototypeChanges(previous.getPrototypeChanges()).setType(Type.STATIC);
- }
- return resultBuilder.build();
- }
-
- public static Builder builder() {
- return new Builder();
- }
-
- public static class Builder extends NestedGraphLens.Builder {
-
- private Map<DexField, DexMethod> getFieldMap = new IdentityHashMap<>();
- private Map<DexField, DexMethod> putFieldMap = new IdentityHashMap<>();
-
- public void mapGetField(DexField from, DexMethod to) {
- getFieldMap.put(from, to);
- }
-
- public void mapPutField(DexField from, DexMethod to) {
- putFieldMap.put(from, to);
- }
-
- public NestedPrivateMethodLens build(AppView<?> appView, DexType nestConstructorType) {
- assert typeMap.isEmpty();
- assert fieldMap.isEmpty();
- if (getFieldMap.isEmpty() && methodMap.isEmpty() && putFieldMap.isEmpty()) {
- return null;
- }
- return new NestedPrivateMethodLens(
- appView, nestConstructorType, methodMap, getFieldMap, putFieldMap, appView.graphLens());
- }
- }
-}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/R8NestBasedAccessDesugaring.java b/src/main/java/com/android/tools/r8/ir/desugar/R8NestBasedAccessDesugaring.java
deleted file mode 100644
index e9ac116..0000000
--- a/src/main/java/com/android/tools/r8/ir/desugar/R8NestBasedAccessDesugaring.java
+++ /dev/null
@@ -1,119 +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.ir.desugar;
-
-import com.android.tools.r8.graph.AppView;
-import com.android.tools.r8.graph.DexApplication;
-import com.android.tools.r8.graph.DexClass;
-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.ProgramMethod;
-import com.android.tools.r8.utils.ThreadUtils;
-import com.google.common.collect.Sets;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-import java.util.concurrent.ExecutionException;
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Future;
-import java.util.function.BiConsumer;
-
-// Summary:
-// - Computes all the live nests reachable from Program Classes (Sequential), each time a
-// nest is computed, its processing starts concurrently.
-// - Add bridges to be processed by further passes and create the maps
-// for the lens (Sequential)
-public class R8NestBasedAccessDesugaring extends NestBasedAccessDesugaring {
-
- public R8NestBasedAccessDesugaring(AppView<?> appView) {
- super(appView);
- }
-
- public NestedPrivateMethodLens run(
- ExecutorService executorService, DexApplication.Builder<?> appBuilder)
- throws ExecutionException {
- assert !appView.options().canUseNestBasedAccess()
- || appView.options().testing.enableForceNestBasedAccessDesugaringForTest;
- computeAndProcessNestsConcurrently(executorService);
- NestedPrivateMethodLens.Builder lensBuilder = NestedPrivateMethodLens.builder();
- addDeferredBridgesAndMapMethods(lensBuilder);
- clearNestAttributes();
- synthesizeNestConstructor(appBuilder);
- return lensBuilder.build(appView, getNestConstructorType());
- }
-
- private void addDeferredBridgesAndMapMethods(NestedPrivateMethodLens.Builder lensBuilder) {
- // Here we add the bridges and we fill the lens map.
- addDeferredBridgesAndMapMethods(bridges, lensBuilder::map);
- addDeferredBridgesAndMapMethods(getFieldBridges, lensBuilder::mapGetField);
- addDeferredBridgesAndMapMethods(putFieldBridges, lensBuilder::mapPutField);
- }
-
- private <E> void addDeferredBridgesAndMapMethods(
- Map<E, ProgramMethod> bridges, BiConsumer<E, DexMethod> lensInserter) {
- for (Map.Entry<E, ProgramMethod> entry : bridges.entrySet()) {
- ProgramMethod method = entry.getValue();
- method.getHolder().addMethod(method.getDefinition());
- lensInserter.accept(entry.getKey(), method.getReference());
- }
- bridges.clear();
- }
-
- private void clearNestAttributes() {
- // Clearing nest attributes is required to allow class merging to be performed across nests
- // and to forbid other optimizations to introduce nest based accesses.
- for (DexClass clazz : appView.appInfo().classes()) {
- clazz.clearNestHost();
- clazz.getNestMembersClassAttributes().clear();
- }
- }
-
- private void computeAndProcessNestsConcurrently(ExecutorService executorService)
- throws ExecutionException {
- Set<DexType> nestHosts = Sets.newIdentityHashSet();
- ;
- List<Future<?>> futures = new ArrayList<>();
- // It is possible that a nest member is on the program path but its nest host
- // is only in the class path (or missing, raising an error).
- // Nests are therefore computed the first time a nest member is met, host or not.
- // The computedNestHosts list is there to avoid processing multiple times the same nest.
- for (DexProgramClass clazz : appView.appInfo().classes()) {
- if (clazz.isInANest()) {
- DexType hostType = clazz.getNestHost();
- if (!nestHosts.contains(hostType)) {
- nestHosts.add(hostType);
- futures.add(asyncProcessNest(clazz, executorService));
- }
- }
- }
- ThreadUtils.awaitFutures(futures);
- }
-
- // In R8, all classes are processed ahead of time.
- @Override
- protected boolean shouldProcessClassInNest(DexClass clazz, List<DexType> nest) {
- return true;
- }
-
- @Override
- void reportMissingNestHost(DexClass clazz) {
- if (appView.options().ignoreMissingClasses) {
- appView.options().nestDesugaringWarningMissingNestHost(clazz);
- } else {
- appView.options().errorMissingClassMissingNestHost(clazz);
- }
- }
-
- @Override
- void reportIncompleteNest(List<DexType> nest) {
- if (appView.options().ignoreMissingClasses) {
- appView.options().nestDesugaringWarningIncompleteNest(nest, appView);
- } else {
- appView.options().errorMissingClassIncompleteNest(nest, appView);
- }
- }
-}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/TwrCloseResourceRewriter.java b/src/main/java/com/android/tools/r8/ir/desugar/TwrCloseResourceRewriter.java
index f7be74d..98acb2d 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/TwrCloseResourceRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/TwrCloseResourceRewriter.java
@@ -4,178 +4,137 @@
package com.android.tools.r8.ir.desugar;
-import com.android.tools.r8.dex.Constants;
-import com.android.tools.r8.graph.AppInfo;
+import com.android.tools.r8.cf.code.CfInstruction;
+import com.android.tools.r8.cf.code.CfInvoke;
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.DexAnnotationSet;
-import com.android.tools.r8.graph.DexApplication.Builder;
-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.DexProto;
-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.GenericSignature.MethodTypeSignature;
import com.android.tools.r8.graph.MethodAccessFlags;
-import com.android.tools.r8.graph.ParameterAnnotationsList;
+import com.android.tools.r8.graph.ProgramMethod;
import com.android.tools.r8.ir.code.IRCode;
-import com.android.tools.r8.ir.code.Instruction;
import com.android.tools.r8.ir.code.InstructionListIterator;
import com.android.tools.r8.ir.code.InvokeStatic;
import com.android.tools.r8.ir.conversion.IRConverter;
import com.android.tools.r8.ir.desugar.backports.BackportedMethods;
-import com.android.tools.r8.origin.SynthesizedOrigin;
-import com.android.tools.r8.shaking.MainDexClasses;
+import com.android.tools.r8.synthesis.SyntheticNaming.SyntheticKind;
import com.android.tools.r8.utils.InternalOptions;
-import com.google.common.collect.Sets;
-import java.util.Collections;
-import java.util.Set;
-import java.util.concurrent.ExecutionException;
-import java.util.concurrent.ExecutorService;
+import com.android.tools.r8.utils.InternalOptions.DesugarState;
+import com.google.common.base.Suppliers;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.function.Consumer;
+import java.util.function.Supplier;
+import org.objectweb.asm.Opcodes;
-// Try with resources outlining processor. Handles $closeResource methods
-// synthesized by java 9 compiler.
+// Try with resources close-resource desugaring.
//
-// Works in two phases:
-// a. during method code processing finds all references to $closeResource(...) synthesized
-// by java compiler, and replaces them with references to a special utility class method.
-// b. after all methods are processed and if there was at least one method referencing
-// $closeResource(...), it synthesizes utility class with appropriate methods.
+// Rewrites $closeResource methods synthesized by java compilers to work on older DEX runtimes.
+// All invocations to $closeResource methods are rewritten to target a compiler generated version
+// that is correct on older DEX runtimes where not all types implement AutoClosable as expected.
//
// Note that we don't remove $closeResource(...) synthesized by java compiler, relying on
// tree shaking to remove them since now they should not be referenced.
-//
+// TODO(b/177401708): D8 does not tree shake so we should remove the now unused method.
public final class TwrCloseResourceRewriter {
- public static final String UTILITY_CLASS_NAME = "$r8$twr$utility";
- public static final String UTILITY_CLASS_DESCRIPTOR = "L$r8$twr$utility;";
-
private final AppView<?> appView;
- private final IRConverter converter;
+ private final DexProto twrCloseResourceProto;
+ private final List<ProgramMethod> synthesizedMethods = new ArrayList<>();
- private final DexMethod twrCloseResourceMethod;
-
- private final Set<DexProgramClass> referencingClasses = Sets.newConcurrentHashSet();
-
- public TwrCloseResourceRewriter(AppView<?> appView, IRConverter converter) {
- this.appView = appView;
- this.converter = converter;
-
- DexItemFactory dexItemFactory = appView.dexItemFactory();
- DexType twrUtilityClass = dexItemFactory.createType(UTILITY_CLASS_DESCRIPTOR);
- DexProto twrCloseResourceProto =
- dexItemFactory.createProto(
- dexItemFactory.voidType, dexItemFactory.throwableType, dexItemFactory.objectType);
- this.twrCloseResourceMethod =
- dexItemFactory.createMethod(
- twrUtilityClass, twrCloseResourceProto, dexItemFactory.twrCloseResourceMethodName);
+ public static boolean enableTwrCloseResourceDesugaring(InternalOptions options) {
+ return options.desugarState == DesugarState.ON
+ && options.enableTryWithResourcesDesugaring()
+ && !options.canUseTwrCloseResourceMethod();
}
- public static boolean isUtilityClassDescriptor(DexType type) {
- return type.descriptor.toString().equals(UTILITY_CLASS_DESCRIPTOR);
+ public TwrCloseResourceRewriter(AppView<?> appView) {
+ this.appView = appView;
+ DexItemFactory dexItemFactory = appView.dexItemFactory();
+ twrCloseResourceProto =
+ dexItemFactory.createProto(
+ dexItemFactory.voidType, dexItemFactory.throwableType, dexItemFactory.objectType);
+ }
+
+ public int rewriteCf(ProgramMethod method, Consumer<ProgramMethod> newMethodCallback) {
+ CfCode code = method.getDefinition().getCode().asCfCode();
+ List<CfInstruction> instructions = code.getInstructions();
+ Supplier<List<CfInstruction>> lazyNewInstructions =
+ Suppliers.memoize(() -> new ArrayList<>(instructions));
+ int replaced = 0;
+ int newInstructionDelta = 0;
+ for (int i = 0; i < instructions.size(); i++) {
+ CfInvoke invoke = instructions.get(i).asInvoke();
+ if (invoke == null
+ || invoke.getOpcode() != Opcodes.INVOKESTATIC
+ || !isTwrCloseResourceMethod(invoke.getMethod(), appView.dexItemFactory())) {
+ continue;
+ }
+ // Synthesize a new method.
+ ProgramMethod closeMethod = createSyntheticCloseResourceMethod(method);
+ newMethodCallback.accept(closeMethod);
+ // Rewrite the invoke to the new synthetic.
+ int newInstructionIndex = i + newInstructionDelta;
+ lazyNewInstructions
+ .get()
+ .set(
+ newInstructionIndex,
+ new CfInvoke(Opcodes.INVOKESTATIC, closeMethod.getReference(), false));
+ ++replaced;
+ }
+ if (replaced > 0) {
+ code.setInstructions(lazyNewInstructions.get());
+ }
+ return replaced;
}
// Rewrites calls to $closeResource() method. Can be invoked concurrently.
- public void rewriteMethodCode(IRCode code) {
+ public void rewriteIR(IRCode code) {
InstructionListIterator iterator = code.instructionListIterator();
- AppInfo appInfo = appView.appInfo();
while (iterator.hasNext()) {
- Instruction instruction = iterator.next();
- if (!instruction.isInvokeStatic()) {
- continue;
- }
- InvokeStatic invoke = instruction.asInvokeStatic();
- if (!isSynthesizedCloseResourceMethod(invoke.getInvokedMethod(), appView)) {
+ InvokeStatic invoke = iterator.next().asInvokeStatic();
+ if (invoke == null
+ || !isTwrCloseResourceMethod(invoke.getInvokedMethod(), appView.dexItemFactory())) {
continue;
}
- // Replace with a call to a synthetic utility with the only
- // implementation of the method $closeResource.
+ // Replace with a call to a synthetic utility.
assert invoke.outValue() == null;
assert invoke.inValues().size() == 2;
- iterator.replaceCurrentInstruction(
- new InvokeStatic(twrCloseResourceMethod, null, invoke.inValues()));
-
- // Mark as a class referencing utility class.
- referencingClasses.add(appInfo.definitionFor(code.method().getHolderType()).asProgramClass());
+ ProgramMethod closeResourceMethod = createSyntheticCloseResourceMethod(code.context());
+ InvokeStatic newInvoke =
+ new InvokeStatic(closeResourceMethod.getReference(), null, invoke.inValues());
+ iterator.replaceCurrentInstruction(newInvoke);
+ synchronized (synthesizedMethods) {
+ synthesizedMethods.add(closeResourceMethod);
+ }
}
}
- public static boolean isSynthesizedCloseResourceMethod(DexMethod method, AppView<?> appView) {
- DexMethod original = appView.graphLens().getOriginalMethodSignature(method);
- assert original != null;
- // We consider all methods of *any* class with expected name and signature
- // to be synthesized by java 9 compiler for try-with-resources, reasoning:
- //
- // * we need to look to all potential classes because the calls might be
- // moved by inlining.
- // * theoretically we could check appropriate encoded method for having
- // right attributes, but it still does not guarantee much since we also
- // need to look into code and doing this seems an overkill
- //
- DexItemFactory dexItemFactory = appView.dexItemFactory();
- return original.name == dexItemFactory.twrCloseResourceMethodName
- && original.proto == dexItemFactory.twrCloseResourceMethodProto;
+ public static boolean isTwrCloseResourceMethod(DexMethod method, DexItemFactory factory) {
+ return method.name == factory.twrCloseResourceMethodName
+ && method.proto == factory.twrCloseResourceMethodProto;
}
- public void synthesizeUtilityClass(
- Builder<?> builder, ExecutorService executorService, InternalOptions options)
- throws ExecutionException {
- if (referencingClasses.isEmpty()) {
- return;
- }
+ private ProgramMethod createSyntheticCloseResourceMethod(ProgramMethod method) {
+ return appView
+ .getSyntheticItems()
+ .createMethod(
+ SyntheticKind.TWR_CLOSE_RESOURCE,
+ method,
+ appView.dexItemFactory(),
+ methodBuilder ->
+ methodBuilder
+ .setAccessFlags(MethodAccessFlags.createPublicStaticSynthetic())
+ .setProto(twrCloseResourceProto)
+ .setCode(
+ m ->
+ BackportedMethods.CloseResourceMethod_closeResourceImpl(
+ appView.options(), m)));
+ }
- // The only encoded method.
- CfCode code =
- BackportedMethods.CloseResourceMethod_closeResourceImpl(options, twrCloseResourceMethod);
- MethodAccessFlags flags =
- MethodAccessFlags.fromSharedAccessFlags(
- Constants.ACC_PUBLIC | Constants.ACC_STATIC | Constants.ACC_SYNTHETIC, false);
- DexEncodedMethod method =
- new DexEncodedMethod(
- twrCloseResourceMethod,
- flags,
- MethodTypeSignature.noSignature(),
- DexAnnotationSet.empty(),
- ParameterAnnotationsList.empty(),
- code,
- true);
-
- // Create utility class.
- DexProgramClass utilityClass =
- new DexProgramClass(
- twrCloseResourceMethod.holder,
- null,
- new SynthesizedOrigin("twr utility class", getClass()),
- ClassAccessFlags.fromSharedAccessFlags(Constants.ACC_PUBLIC | Constants.ACC_SYNTHETIC),
- appView.dexItemFactory().objectType,
- DexTypeList.empty(),
- null,
- null,
- Collections.emptyList(),
- null,
- Collections.emptyList(),
- ClassSignature.noSignature(),
- DexAnnotationSet.empty(),
- DexEncodedField.EMPTY_ARRAY,
- DexEncodedField.EMPTY_ARRAY,
- new DexEncodedMethod[] {method},
- DexEncodedMethod.EMPTY_ARRAY,
- appView.dexItemFactory().getSkipNameValidationForTesting(),
- DexProgramClass::checksumFromType,
- referencingClasses);
-
- // Process created class and method.
- AppInfo appInfo = appView.appInfo();
- MainDexClasses mainDexClasses = appInfo.getMainDexClasses();
- boolean addToMainDexList = mainDexClasses.containsAnyOf(referencingClasses);
- appInfo.addSynthesizedClass(utilityClass, addToMainDexList);
- converter.optimizeSynthesizedClass(utilityClass, executorService);
- builder.addSynthesizedClass(utilityClass);
+ public void processSynthesizedMethods(IRConverter converter) {
+ synthesizedMethods.forEach(converter::optimizeSynthesizedMethod);
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/nest/D8NestBasedAccessDesugaring.java b/src/main/java/com/android/tools/r8/ir/desugar/nest/D8NestBasedAccessDesugaring.java
new file mode 100644
index 0000000..9fcf485
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/desugar/nest/D8NestBasedAccessDesugaring.java
@@ -0,0 +1,178 @@
+// 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.desugar.nest;
+
+import static com.android.tools.r8.ir.desugar.InterfaceMethodRewriter.reportDependencyEdge;
+
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.ClasspathMethod;
+import com.android.tools.r8.graph.DexClass;
+import com.android.tools.r8.graph.DexClassAndField;
+import com.android.tools.r8.graph.DexClassAndMethod;
+import com.android.tools.r8.graph.DexClasspathClass;
+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.graph.UseRegistry;
+import com.android.tools.r8.ir.conversion.MethodProcessor;
+import com.android.tools.r8.utils.ThreadUtils;
+import com.google.common.collect.Iterables;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorService;
+
+/**
+ * Responsible for reporting desugar dependencies and for synthesizing bridges in the program for
+ * accesses from the classpath into the program.
+ */
+public class D8NestBasedAccessDesugaring extends NestBasedAccessDesugaring {
+
+ public D8NestBasedAccessDesugaring(AppView<?> appView) {
+ super(appView);
+ }
+
+ public void reportDesugarDependencies() {
+ forEachNest(
+ nest -> {
+ if (nest.hasMissingMembers()) {
+ throw appView.options().errorMissingNestMember(nest);
+ }
+ DexClass hostClass = nest.getHostClass();
+ for (DexClass memberClass : nest.getMembers()) {
+ if (hostClass.isProgramClass() || memberClass.isProgramClass()) {
+ reportDependencyEdge(hostClass, memberClass, appView.options());
+ reportDependencyEdge(memberClass, hostClass, appView.options());
+ }
+ }
+ },
+ classWithoutHost -> {
+ throw appView.options().errorMissingNestHost(classWithoutHost);
+ });
+ }
+
+ public void synthesizeBridgesForNestBasedAccessesOnClasspath(
+ MethodProcessor methodProcessor, ExecutorService executorService) throws ExecutionException {
+ List<DexClasspathClass> classpathClassesInNests = new ArrayList<>();
+ forEachNest(
+ nest -> {
+ if (nest.getHostClass().isClasspathClass()) {
+ classpathClassesInNests.add(nest.getHostClass().asClasspathClass());
+ }
+ Iterables.addAll(classpathClassesInNests, nest.getClasspathMembers());
+ });
+
+ NestBridgeConsumer bridgeConsumer = NestBridgeConsumer.createForD8(methodProcessor);
+ ThreadUtils.processItems(
+ classpathClassesInNests,
+ clazz -> synthesizeBridgesForNestBasedAccessesOnClasspath(clazz, bridgeConsumer),
+ executorService);
+ }
+
+ public void synthesizeBridgesForNestBasedAccessesOnClasspath(
+ DexClasspathClass clazz, NestBridgeConsumer bridgeConsumer) {
+ clazz.forEachClasspathMethod(
+ method ->
+ method.registerCodeReferencesForDesugaring(
+ new NestBasedAccessDesugaringUseRegistry(method, bridgeConsumer)));
+ }
+
+ private class NestBasedAccessDesugaringUseRegistry extends UseRegistry {
+
+ private final NestBridgeConsumer bridgeConsumer;
+ private final ClasspathMethod context;
+
+ NestBasedAccessDesugaringUseRegistry(
+ ClasspathMethod context, NestBridgeConsumer bridgeConsumer) {
+ super(appView.dexItemFactory());
+ this.bridgeConsumer = bridgeConsumer;
+ this.context = context;
+ }
+
+ private void registerFieldAccess(DexField reference, boolean isGet) {
+ DexClassAndField field =
+ reference.lookupMemberOnClass(appView.definitionForHolder(reference));
+ if (field != null && needsDesugaring(field, context)) {
+ ensureFieldAccessBridge(field, isGet, bridgeConsumer);
+ }
+ }
+
+ private void registerInvoke(DexMethod reference) {
+ if (!reference.getHolderType().isClassType()) {
+ return;
+ }
+ DexClassAndMethod method =
+ reference.lookupMemberOnClass(appView.definitionForHolder(reference));
+ if (method != null && needsDesugaring(method, context)) {
+ ensureMethodBridge(method, bridgeConsumer);
+ }
+ }
+
+ @Override
+ public void registerInvokeDirect(DexMethod method) {
+ registerInvoke(method);
+ }
+
+ @Override
+ public void registerInvokeInterface(DexMethod method) {
+ registerInvoke(method);
+ }
+
+ @Override
+ public void registerInvokeStatic(DexMethod method) {
+ registerInvoke(method);
+ }
+
+ @Override
+ public void registerInvokeSuper(DexMethod method) {
+ registerInvoke(method);
+ }
+
+ @Override
+ public void registerInvokeVirtual(DexMethod method) {
+ registerInvoke(method);
+ }
+
+ @Override
+ public void registerInstanceFieldWrite(DexField field) {
+ registerFieldAccess(field, false);
+ }
+
+ @Override
+ public void registerInstanceFieldRead(DexField field) {
+ registerFieldAccess(field, true);
+ }
+
+ @Override
+ public void registerStaticFieldRead(DexField field) {
+ registerFieldAccess(field, true);
+ }
+
+ @Override
+ public void registerStaticFieldWrite(DexField field) {
+ registerFieldAccess(field, false);
+ }
+
+ @Override
+ public void registerInitClass(DexType clazz) {
+ // Intentionally empty.
+ }
+
+ @Override
+ public void registerInstanceOf(DexType type) {
+ // Intentionally empty.
+ }
+
+ @Override
+ public void registerNewInstance(DexType type) {
+ // Intentionally empty.
+ }
+
+ @Override
+ public void registerTypeReference(DexType type) {
+ // Intentionally empty.
+ }
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/nest/D8NestBridgeConsumer.java b/src/main/java/com/android/tools/r8/ir/desugar/nest/D8NestBridgeConsumer.java
new file mode 100644
index 0000000..7468cba
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/desugar/nest/D8NestBridgeConsumer.java
@@ -0,0 +1,33 @@
+// 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.desugar.nest;
+
+import com.android.tools.r8.graph.ProgramField;
+import com.android.tools.r8.graph.ProgramMethod;
+import com.android.tools.r8.ir.conversion.MethodProcessor;
+
+public class D8NestBridgeConsumer extends NestBridgeConsumer {
+
+ private final MethodProcessor methodProcessor;
+
+ public D8NestBridgeConsumer(MethodProcessor methodProcessor) {
+ this.methodProcessor = methodProcessor;
+ }
+
+ @Override
+ public void acceptFieldGetBridge(ProgramField target, ProgramMethod bridge) {
+ methodProcessor.scheduleMethodForProcessingAfterCurrentWave(bridge);
+ }
+
+ @Override
+ public void acceptFieldPutBridge(ProgramField target, ProgramMethod bridge) {
+ methodProcessor.scheduleMethodForProcessingAfterCurrentWave(bridge);
+ }
+
+ @Override
+ public void acceptMethodBridge(ProgramMethod target, ProgramMethod bridge) {
+ methodProcessor.scheduleMethodForProcessingAfterCurrentWave(bridge);
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/nest/Nest.java b/src/main/java/com/android/tools/r8/ir/desugar/nest/Nest.java
new file mode 100644
index 0000000..979eb9c
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/desugar/nest/Nest.java
@@ -0,0 +1,83 @@
+// 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.desugar.nest;
+
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexClass;
+import com.android.tools.r8.graph.DexClasspathClass;
+import com.android.tools.r8.graph.DexType;
+import com.google.common.collect.Iterables;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.function.Consumer;
+
+public class Nest {
+
+ private final DexClass hostClass;
+ private final List<DexClass> members;
+ private final List<DexType> missingMembers;
+
+ private Nest(DexClass hostClass, List<DexClass> members, List<DexType> missingMembers) {
+ this.hostClass = hostClass;
+ this.members = members;
+ this.missingMembers = missingMembers;
+ }
+
+ public static Nest create(AppView<?> appView, DexClass clazz) {
+ return create(appView, clazz, null);
+ }
+
+ public static Nest create(
+ AppView<?> appView, DexClass clazz, Consumer<DexClass> missingHostConsumer) {
+ assert clazz.isInANest();
+
+ DexClass hostClass = clazz.isNestHost() ? clazz : appView.definitionFor(clazz.getNestHost());
+ if (hostClass == null) {
+ // Missing nest host means the class is considered as not being part of a nest.
+ if (missingHostConsumer != null) {
+ missingHostConsumer.accept(clazz);
+ }
+ return null;
+ }
+
+ List<DexClass> members = new ArrayList<>(hostClass.getNestMembersClassAttributes().size());
+ List<DexType> missingMembers = new ArrayList<>();
+ hostClass.forEachNestMember(
+ memberType -> {
+ DexClass memberClass = appView.definitionFor(memberType);
+ if (memberClass != null) {
+ members.add(memberClass);
+ } else {
+ missingMembers.add(memberType);
+ }
+ });
+ return new Nest(hostClass, members, missingMembers);
+ }
+
+ public Iterable<DexClasspathClass> getClasspathMembers() {
+ return Iterables.transform(
+ Iterables.filter(members, DexClass::isClasspathClass), DexClass::asClasspathClass);
+ }
+
+ public DexClass getHostClass() {
+ return hostClass;
+ }
+
+ public List<DexClass> getMembers() {
+ return members;
+ }
+
+ public List<DexType> getMissingMembers() {
+ return missingMembers;
+ }
+
+ public boolean hasLibraryMember() {
+ return Iterables.any(members, DexClass::isLibraryClass);
+ }
+
+ public boolean hasMissingMembers() {
+ return !missingMembers.isEmpty();
+ }
+}
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
new file mode 100644
index 0000000..cbaa05a
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/desugar/nest/NestBasedAccessDesugaring.java
@@ -0,0 +1,382 @@
+// 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.desugar.nest;
+
+import static com.android.tools.r8.utils.ConsumerUtils.emptyConsumer;
+
+import com.android.tools.r8.cf.code.CfConstNull;
+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;
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.graph.DexProto;
+import com.android.tools.r8.graph.DexString;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.DexTypeList;
+import com.android.tools.r8.graph.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.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.Set;
+import java.util.function.Consumer;
+import org.objectweb.asm.Opcodes;
+
+// NestBasedAccessDesugaring contains common code between the two subclasses
+// which are specialized for d8 and r8
+public class NestBasedAccessDesugaring {
+
+ // Short names to avoid creating long strings
+ public static final String NEST_ACCESS_NAME_PREFIX = "-$$Nest$";
+ private static final String NEST_ACCESS_METHOD_NAME_PREFIX = NEST_ACCESS_NAME_PREFIX + "m";
+ private static final String NEST_ACCESS_STATIC_METHOD_NAME_PREFIX =
+ NEST_ACCESS_NAME_PREFIX + "sm";
+ private static final String NEST_ACCESS_FIELD_GET_NAME_PREFIX = NEST_ACCESS_NAME_PREFIX + "fget";
+ private static final String NEST_ACCESS_STATIC_GET_FIELD_NAME_PREFIX =
+ NEST_ACCESS_NAME_PREFIX + "sfget";
+ 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;
+
+ public NestBasedAccessDesugaring(AppView<?> appView) {
+ this.appView = appView;
+ this.dexItemFactory = appView.dexItemFactory();
+ }
+
+ void forEachNest(Consumer<Nest> consumer) {
+ forEachNest(consumer, emptyConsumer());
+ }
+
+ void forEachNest(Consumer<Nest> consumer, Consumer<DexClass> missingHostConsumer) {
+ Set<DexType> seenNestHosts = Sets.newIdentityHashSet();
+ for (DexProgramClass clazz : appView.appInfo().classes()) {
+ if (!clazz.isInANest() || !seenNestHosts.add(clazz.getNestHost())) {
+ continue;
+ }
+
+ Nest nest = Nest.create(appView, clazz, missingHostConsumer);
+ if (nest != null) {
+ consumer.accept(nest);
+ }
+ }
+ }
+
+ public boolean needsDesugaring(ProgramMethod method) {
+ if (!method.getHolder().isInANest() || !method.getDefinition().hasCode()) {
+ return false;
+ }
+
+ Code code = method.getDefinition().getCode();
+ if (code.isDexCode()) {
+ assert appView.testing().allowDexInputForTesting;
+ return false;
+ }
+
+ if (!code.isCfCode()) {
+ throw new Unreachable("Unexpected attempt to determine if non-CF code needs desugaring");
+ }
+
+ return Iterables.any(
+ code.asCfCode().getInstructions(), instruction -> needsDesugaring(instruction, method));
+ }
+
+ private boolean needsDesugaring(CfInstruction instruction, ProgramMethod context) {
+ if (instruction.isFieldInstruction()) {
+ return needsDesugaring(instruction.asFieldInstruction().getField(), context);
+ }
+ if (instruction.isInvoke()) {
+ return needsDesugaring(instruction.asInvoke().getMethod(), context);
+ }
+ return false;
+ }
+
+ public boolean needsDesugaring(DexMember<?, ?> memberReference, ProgramMethod context) {
+ if (!context.getHolder().isInANest() || !memberReference.getHolderType().isClassType()) {
+ return false;
+ }
+ DexClass holder = appView.definitionForHolder(memberReference, context);
+ DexClassAndMember<?, ?> member = memberReference.lookupMemberOnClass(holder);
+ return member != null && needsDesugaring(member, context);
+ }
+
+ public boolean needsDesugaring(DexClassAndMember<?, ?> member, DexClassAndMethod context) {
+ return member.getAccessFlags().isPrivate()
+ && member.getHolderType() != context.getHolderType()
+ && member.getHolder().isInANest()
+ && member.getHolder().getNestHost() == context.getHolder().getNestHost();
+ }
+
+ public boolean desugar(ProgramMethod method) {
+ return desugar(method, null);
+ }
+
+ public boolean desugar(ProgramMethod method, NestBridgeConsumer bridgeConsumer) {
+ if (!method.getHolder().isInANest()) {
+ return false;
+ }
+
+ Code code = method.getDefinition().getCode();
+ if (!code.isCfCode()) {
+ appView
+ .options()
+ .reporter
+ .error(
+ new StringDiagnostic(
+ "Unsupported attempt to desugar non-CF code",
+ method.getOrigin(),
+ method.getPosition()));
+ return false;
+ }
+
+ CfCode cfCode = code.asCfCode();
+ List<CfInstruction> desugaredInstructions =
+ ListUtils.flatMap(
+ cfCode.getInstructions(),
+ instruction -> desugarInstruction(instruction, method, bridgeConsumer),
+ null);
+ if (desugaredInstructions != null) {
+ cfCode.setInstructions(desugaredInstructions);
+ return true;
+ }
+ return false;
+ }
+
+ public List<CfInstruction> desugarInstruction(
+ CfInstruction instruction, ProgramMethod context, NestBridgeConsumer bridgeConsumer) {
+ if (instruction.isFieldInstruction()) {
+ return desugarFieldInstruction(instruction.asFieldInstruction(), context, bridgeConsumer);
+ }
+ if (instruction.isInvoke()) {
+ return desugarInvokeInstruction(instruction.asInvoke(), context, bridgeConsumer);
+ }
+ return null;
+ }
+
+ private List<CfInstruction> desugarFieldInstruction(
+ CfFieldInstruction instruction, ProgramMethod context, NestBridgeConsumer bridgeConsumer) {
+ // Since we only need to desugar accesses to private fields, and all accesses to private
+ // fields must be accessing the private field directly on its holder, we can lookup the
+ // field on the holder instead of resolving the field.
+ DexClass holder = appView.definitionForHolder(instruction.getField(), context);
+ DexClassAndField field = instruction.getField().lookupMemberOnClass(holder);
+ if (field == null || !needsDesugaring(field, context)) {
+ return null;
+ }
+
+ DexMethod bridge = ensureFieldAccessBridge(field, instruction.isFieldGet(), bridgeConsumer);
+ return ImmutableList.of(
+ new CfInvoke(Opcodes.INVOKESTATIC, bridge, field.getHolder().isInterface()));
+ }
+
+ private List<CfInstruction> desugarInvokeInstruction(
+ CfInvoke invoke, ProgramMethod context, NestBridgeConsumer bridgeConsumer) {
+ DexMethod invokedMethod = invoke.getMethod();
+ if (!invokedMethod.getHolderType().isClassType()) {
+ return null;
+ }
+
+ // Since we only need to desugar accesses to private methods, and all accesses to private
+ // methods must be accessing the private method directly on its holder, we can lookup the
+ // method on the holder instead of resolving the method.
+ DexClass holder = appView.definitionForHolder(invokedMethod, context);
+ DexClassAndMethod target = invokedMethod.lookupMemberOnClass(holder);
+ if (target == null || !needsDesugaring(target, context)) {
+ return null;
+ }
+
+ DexMethod bridge = ensureMethodBridge(target, bridgeConsumer);
+ if (target.getDefinition().isInstanceInitializer()) {
+ assert !invoke.isInterface();
+ return ImmutableList.of(
+ new CfConstNull(), new CfInvoke(Opcodes.INVOKESPECIAL, bridge, false));
+ }
+
+ return ImmutableList.of(new CfInvoke(Opcodes.INVOKESTATIC, bridge, invoke.isInterface()));
+ }
+
+ private RuntimeException reportIncompleteNest(LibraryMember<?, ?> member) {
+ Nest nest = Nest.create(appView, member.getHolder());
+ assert nest != null : "Should be a compilation error if missing nest host on library class.";
+ 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()) {
+ return ensureFieldAccessBridge(field.asProgramField(), isGet, bridgeConsumer);
+ }
+ if (field.isClasspathField()) {
+ return getFieldAccessBridgeReference(field, isGet);
+ }
+ assert field.isLibraryField();
+ throw reportIncompleteNest(field.asLibraryField());
+ }
+
+ private DexMethod ensureFieldAccessBridge(
+ ProgramField field, boolean isGet, NestBridgeConsumer bridgeConsumer) {
+ DexMethod bridgeReference = getFieldAccessBridgeReference(field, isGet);
+ synchronized (field.getHolder().getMethodCollection()) {
+ ProgramMethod bridge = field.getHolder().lookupProgramMethod(bridgeReference);
+ if (bridge == null) {
+ bridge = DexEncodedMethod.createFieldAccessorBridge(field, isGet, bridgeReference);
+ bridge.getHolder().addDirectMethod(bridge.getDefinition());
+ if (bridgeConsumer != null) {
+ bridgeConsumer.acceptFieldBridge(field, bridge, isGet);
+ }
+ }
+ return bridge.getReference();
+ }
+ }
+
+ private DexMethod getFieldAccessBridgeReference(DexClassAndField field, boolean isGet) {
+ int bridgeParameterCount =
+ BooleanUtils.intValue(!field.getAccessFlags().isStatic()) + BooleanUtils.intValue(!isGet);
+ DexType[] parameters = new DexType[bridgeParameterCount];
+ if (!isGet) {
+ parameters[parameters.length - 1] = field.getType();
+ }
+ if (!field.getAccessFlags().isStatic()) {
+ parameters[0] = field.getHolderType();
+ }
+ DexType returnType = isGet ? field.getType() : dexItemFactory.voidType;
+ DexProto proto = dexItemFactory.createProto(returnType, parameters);
+ return dexItemFactory.createMethod(
+ field.getHolderType(), proto, getFieldAccessBridgeName(field, isGet));
+ }
+
+ private DexString getFieldAccessBridgeName(DexClassAndField field, boolean isGet) {
+ String prefix;
+ if (isGet && !field.getAccessFlags().isStatic()) {
+ prefix = NEST_ACCESS_FIELD_GET_NAME_PREFIX;
+ } else if (isGet) {
+ prefix = NEST_ACCESS_STATIC_GET_FIELD_NAME_PREFIX;
+ } else if (!field.getAccessFlags().isStatic()) {
+ prefix = NEST_ACCESS_FIELD_PUT_NAME_PREFIX;
+ } else {
+ prefix = NEST_ACCESS_STATIC_PUT_FIELD_NAME_PREFIX;
+ }
+ return dexItemFactory.createString(prefix + field.getName().toString());
+ }
+
+ DexMethod ensureMethodBridge(DexClassAndMethod method, NestBridgeConsumer bridgeConsumer) {
+ if (method.isProgramMethod()) {
+ return ensureMethodBridge(method.asProgramMethod(), bridgeConsumer);
+ }
+ if (method.isClasspathMethod()) {
+ return getMethodBridgeReference(method);
+ }
+ assert method.isLibraryMethod();
+ throw reportIncompleteNest(method.asLibraryMethod());
+ }
+
+ private DexMethod ensureMethodBridge(ProgramMethod method, NestBridgeConsumer bridgeConsumer) {
+ DexMethod bridgeReference = getMethodBridgeReference(method);
+ synchronized (method.getHolder().getMethodCollection()) {
+ ProgramMethod bridge = method.getHolder().lookupProgramMethod(bridgeReference);
+ if (bridge == null) {
+ DexEncodedMethod definition = method.getDefinition();
+ bridge =
+ definition.isInstanceInitializer()
+ ? definition.toInitializerForwardingBridge(
+ method.getHolder(), bridgeReference, dexItemFactory)
+ : definition.toStaticForwardingBridge(
+ method.getHolder(), bridgeReference, dexItemFactory);
+ bridge.getHolder().addDirectMethod(bridge.getDefinition());
+ if (bridgeConsumer != null) {
+ bridgeConsumer.acceptMethodBridge(method, bridge);
+ }
+ }
+ }
+ return bridgeReference;
+ }
+
+ private DexMethod getMethodBridgeReference(DexClassAndMethod method) {
+ if (method.getDefinition().isInstanceInitializer()) {
+ DexProto newProto =
+ dexItemFactory.appendTypeToProto(method.getProto(), dexItemFactory.nestConstructorType);
+ nestConstructorUsed = true;
+ return method.getReference().withProto(newProto, dexItemFactory);
+ }
+ DexProto proto =
+ method.getAccessFlags().isStatic()
+ ? method.getProto()
+ : dexItemFactory.prependHolderToProto(method.getReference());
+ return dexItemFactory.createMethod(method.getHolderType(), proto, getMethodBridgeName(method));
+ }
+
+ private DexString getMethodBridgeName(DexClassAndMethod method) {
+ String prefix =
+ method.getAccessFlags().isStatic()
+ ? NEST_ACCESS_STATIC_METHOD_NAME_PREFIX
+ : NEST_ACCESS_METHOD_NAME_PREFIX;
+ return dexItemFactory.createString(prefix + method.getName().toString());
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/nest/NestBridgeConsumer.java b/src/main/java/com/android/tools/r8/ir/desugar/nest/NestBridgeConsumer.java
new file mode 100644
index 0000000..e6a8a03
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/desugar/nest/NestBridgeConsumer.java
@@ -0,0 +1,30 @@
+// 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.desugar.nest;
+
+import com.android.tools.r8.graph.ProgramField;
+import com.android.tools.r8.graph.ProgramMethod;
+import com.android.tools.r8.ir.conversion.MethodProcessor;
+
+public abstract class NestBridgeConsumer {
+
+ public static D8NestBridgeConsumer createForD8(MethodProcessor methodProcessor) {
+ return new D8NestBridgeConsumer(methodProcessor);
+ }
+
+ public abstract void acceptFieldGetBridge(ProgramField target, ProgramMethod bridge);
+
+ public abstract void acceptFieldPutBridge(ProgramField target, ProgramMethod bridge);
+
+ public abstract void acceptMethodBridge(ProgramMethod target, ProgramMethod bridge);
+
+ public final void acceptFieldBridge(ProgramField target, ProgramMethod bridge, boolean isGet) {
+ if (isGet) {
+ acceptFieldGetBridge(target, bridge);
+ } else {
+ acceptFieldPutBridge(target, bridge);
+ }
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/AssumeInserter.java b/src/main/java/com/android/tools/r8/ir/optimize/AssumeInserter.java
index d6eae9a..ddb7ff5 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/AssumeInserter.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/AssumeInserter.java
@@ -6,11 +6,12 @@
import static com.android.tools.r8.ir.analysis.type.Nullability.definitelyNotNull;
import static com.google.common.base.Predicates.alwaysTrue;
-import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexClassAndField;
import com.android.tools.r8.graph.DexClassAndMethod;
-import com.android.tools.r8.graph.DexEncodedField;
import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.FieldResolutionResult.SuccessfulFieldResolutionResult;
+import com.android.tools.r8.graph.ResolutionResult.SingleResolutionResult;
import com.android.tools.r8.ir.analysis.type.ClassTypeElement;
import com.android.tools.r8.ir.analysis.type.TypeAnalysis;
import com.android.tools.r8.ir.analysis.type.TypeElement;
@@ -33,7 +34,9 @@
import com.android.tools.r8.ir.code.Value;
import com.android.tools.r8.ir.optimize.info.FieldOptimizationInfo;
import com.android.tools.r8.ir.optimize.info.MethodOptimizationInfo;
-import com.android.tools.r8.utils.InternalOptions;
+import com.android.tools.r8.ir.optimize.membervaluepropagation.assume.AssumeInfo;
+import com.android.tools.r8.ir.optimize.membervaluepropagation.assume.AssumeInfoLookup;
+import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.android.tools.r8.utils.Timing;
import com.android.tools.r8.utils.TriConsumer;
import com.android.tools.r8.utils.TriFunction;
@@ -56,12 +59,10 @@
public class AssumeInserter {
- private final AppView<? extends AppInfoWithClassHierarchy> appView;
- private final InternalOptions options;
+ private final AppView<AppInfoWithLiveness> appView;
- public AssumeInserter(AppView<? extends AppInfoWithClassHierarchy> appView) {
+ public AssumeInserter(AppView<AppInfoWithLiveness> appView) {
this.appView = appView;
- this.options = appView.options();
}
public void insertAssumeInstructions(IRCode code, Timing timing) {
@@ -225,7 +226,26 @@
private boolean computeAssumedValuesFromSingleTarget(
IRCode code, InvokeMethod invoke, AssumedValues.Builder assumedValuesBuilder) {
+ SingleResolutionResult resolutionResult =
+ appView
+ .appInfo()
+ .unsafeResolveMethodDueToDexFormat(invoke.getInvokedMethod())
+ .asSingleResolution();
+ if (resolutionResult == null) {
+ return false;
+ }
+
DexClassAndMethod singleTarget = invoke.lookupSingleTarget(appView, code.context());
+ if (invoke.hasUsedOutValue() && invoke.getOutType().isReferenceType()) {
+ AssumeInfo assumeInfo =
+ AssumeInfoLookup.lookupAssumeInfo(appView, resolutionResult, singleTarget);
+ if (assumeInfo != null
+ && assumeInfo.hasReturnInfo()
+ && assumeInfo.getReturnInfo().isNonNull()) {
+ assumedValuesBuilder.addNonNullValueKnownToDominateAllUsers(invoke, invoke.outValue());
+ }
+ }
+
if (singleTarget == null) {
return false;
}
@@ -234,12 +254,11 @@
MethodOptimizationInfo optimizationInfo = singleTarget.getDefinition().getOptimizationInfo();
// Case (2), invocations that are guaranteed to return a non-null value.
- Value outValue = invoke.outValue();
- if (outValue != null && outValue.hasNonDebugUsers()) {
+ if (invoke.hasUsedOutValue()) {
needsAssumeInstruction =
computeAssumedValuesForOutValue(
invoke,
- optimizationInfo.getDynamicUpperBoundTypeOrElse(outValue.getType()),
+ optimizationInfo.getDynamicUpperBoundTypeOrElse(invoke.getOutType()),
optimizationInfo.getDynamicLowerBoundType(),
assumedValuesBuilder);
}
@@ -264,20 +283,31 @@
private boolean computeAssumedValuesForFieldGet(
FieldInstruction fieldGet, AssumedValues.Builder assumedValuesBuilder) {
- Value outValue = fieldGet.outValue();
- if (!outValue.hasNonDebugUsers()) {
+ if (fieldGet.hasUnusedOutValue()) {
return false;
}
- DexEncodedField field = appView.appInfo().resolveField(fieldGet.getField()).getResolvedField();
- if (field == null) {
+ SuccessfulFieldResolutionResult resolutionResult =
+ appView.appInfo().resolveField(fieldGet.getField()).asSuccessfulResolution();
+ if (resolutionResult == null) {
return false;
}
- FieldOptimizationInfo optimizationInfo = field.getOptimizationInfo();
+ DexClassAndField field = resolutionResult.getResolutionPair();
+
+ if (field.getType().isReferenceType()) {
+ AssumeInfo assumeInfo = AssumeInfoLookup.lookupAssumeInfo(appView, field);
+ if (assumeInfo != null
+ && assumeInfo.hasReturnInfo()
+ && assumeInfo.getReturnInfo().isNonNull()) {
+ assumedValuesBuilder.addNonNullValueKnownToDominateAllUsers(fieldGet, fieldGet.outValue());
+ }
+ }
+
+ FieldOptimizationInfo optimizationInfo = field.getDefinition().getOptimizationInfo();
return computeAssumedValuesForOutValue(
fieldGet,
- optimizationInfo.getDynamicUpperBoundTypeOrElse(outValue.getType()),
+ optimizationInfo.getDynamicUpperBoundTypeOrElse(fieldGet.getOutType()),
optimizationInfo.getDynamicLowerBoundType(),
assumedValuesBuilder);
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java b/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java
index 89d9eb7..5112116 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java
@@ -954,7 +954,10 @@
return rewriteSwitch(code, SwitchCaseAnalyzer.getInstance());
}
- public boolean rewriteSwitch(IRCode code, SwitchCaseAnalyzer switchCaseAnalyzer) {
+ private boolean rewriteSwitch(IRCode code, SwitchCaseAnalyzer switchCaseAnalyzer) {
+ if (!options.isSwitchRewritingEnabled()) {
+ return false;
+ }
if (!code.metadata().mayHaveSwitch()) {
return false;
}
@@ -1473,19 +1476,16 @@
UtilityMethodForCodeOptimizations throwClassCastExceptionIfNotNullMethod =
UtilityMethodsForCodeOptimizations.synthesizeThrowClassCastExceptionIfNotNullMethod(
appView, context, methodProcessingId);
- // TODO(b/172194277): Allow synthetics when generating CF.
- if (throwClassCastExceptionIfNotNullMethod != null) {
- throwClassCastExceptionIfNotNullMethod.optimize(methodProcessor);
- InvokeStatic replacement =
- InvokeStatic.builder()
- .setMethod(throwClassCastExceptionIfNotNullMethod.getMethod())
- .setSingleArgument(checkCast.object())
- .setPosition(checkCast)
- .build();
- it.replaceCurrentInstruction(replacement);
- assert replacement.lookupSingleTarget(appView, context) != null;
- return RemoveCheckCastInstructionIfTrivialResult.REMOVED_CAST_DO_NARROW;
- }
+ throwClassCastExceptionIfNotNullMethod.optimize(methodProcessor);
+ InvokeStatic replacement =
+ InvokeStatic.builder()
+ .setMethod(throwClassCastExceptionIfNotNullMethod.getMethod())
+ .setSingleArgument(checkCast.object())
+ .setPosition(checkCast)
+ .build();
+ it.replaceCurrentInstruction(replacement);
+ assert replacement.lookupSingleTarget(appView, context) != null;
+ return RemoveCheckCastInstructionIfTrivialResult.REMOVED_CAST_DO_NARROW;
}
// Otherwise, keep the checkcast to preserve verification errors. E.g., down-cast:
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 674488f..7179327 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,6 +25,7 @@
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;
@@ -47,10 +48,13 @@
public class Devirtualizer {
private final AppView<AppInfoWithLiveness> appView;
+ private final MainDexTracingResult mainDexTracingResult;
private final InternalOptions options;
- public Devirtualizer(AppView<AppInfoWithLiveness> appView) {
+ public Devirtualizer(
+ AppView<AppInfoWithLiveness> appView, MainDexTracingResult mainDexTracingResult) {
this.appView = appView;
+ this.mainDexTracingResult = mainDexTracingResult;
this.options = appView.options();
}
@@ -148,7 +152,9 @@
// Rebind the invoke to the most specific target.
DexMethod invokedMethod = invoke.getInvokedMethod();
DexClassAndMethod reboundTarget = rebindSuperInvokeToMostSpecific(invokedMethod, context);
- if (reboundTarget != null && reboundTarget.getReference() != invokedMethod) {
+ if (reboundTarget != null
+ && reboundTarget.getReference() != invokedMethod
+ && !isRebindingNewClassIntoMainDex(invokedMethod, reboundTarget.getReference())) {
it.replaceCurrentInstruction(
new InvokeSuper(
reboundTarget.getReference(),
@@ -190,6 +196,11 @@
continue;
}
+ // Ensure that we are not adding a new main dex root
+ if (isRebindingNewClassIntoMainDex(invoke.getInvokedMethod(), target.getReference())) {
+ continue;
+ }
+
InvokeVirtual devirtualizedInvoke =
new InvokeVirtual(target.getReference(), invoke.outValue(), invoke.inValues());
it.replaceCurrentInstruction(devirtualizedInvoke);
@@ -382,4 +393,14 @@
// Change the invoke-virtual instruction to target the refined resolution result instead.
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);
+ }
}
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 1cce76e..38c4e6f 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
@@ -49,7 +49,6 @@
import com.android.tools.r8.ir.conversion.LensCodeRewriter;
import com.android.tools.r8.ir.conversion.MethodProcessor;
import com.android.tools.r8.ir.conversion.PostOptimization;
-import com.android.tools.r8.ir.desugar.TwrCloseResourceRewriter;
import com.android.tools.r8.ir.optimize.info.OptimizationFeedback;
import com.android.tools.r8.ir.optimize.info.OptimizationFeedbackIgnore;
import com.android.tools.r8.ir.optimize.inliner.DefaultInliningReasonStrategy;
@@ -130,9 +129,7 @@
}
if (extraNeverInlineMethods.contains(
- appView.graphLens().getOriginalMethodSignature(singleTargetReference))
- || TwrCloseResourceRewriter.isSynthesizedCloseResourceMethod(
- singleTargetReference, appView)) {
+ appView.graphLens().getOriginalMethodSignature(singleTargetReference))) {
whyAreYouNotInliningReporter.reportExtraNeverInline();
return true;
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/MemberValuePropagation.java b/src/main/java/com/android/tools/r8/ir/optimize/MemberValuePropagation.java
index 2950d4c..d92d375 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/MemberValuePropagation.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/MemberValuePropagation.java
@@ -7,19 +7,20 @@
import static com.google.common.base.Predicates.alwaysTrue;
import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexClassAndField;
import com.android.tools.r8.graph.DexClassAndMethod;
-import com.android.tools.r8.graph.DexDefinition;
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.DexReference;
import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.FieldResolutionResult.SuccessfulFieldResolutionResult;
import com.android.tools.r8.graph.ProgramMethod;
+import com.android.tools.r8.graph.ResolutionResult.SingleResolutionResult;
import com.android.tools.r8.ir.analysis.type.TypeAnalysis;
import com.android.tools.r8.ir.analysis.type.TypeElement;
import com.android.tools.r8.ir.analysis.value.AbstractValue;
import com.android.tools.r8.ir.analysis.value.SingleValue;
+import com.android.tools.r8.ir.analysis.value.UnknownValue;
import com.android.tools.r8.ir.code.BasicBlock;
import com.android.tools.r8.ir.code.FieldInstruction;
import com.android.tools.r8.ir.code.IRCode;
@@ -34,8 +35,9 @@
import com.android.tools.r8.ir.code.Value;
import com.android.tools.r8.ir.optimize.info.OptimizationFeedback;
import com.android.tools.r8.ir.optimize.info.OptimizationFeedbackSimple;
+import com.android.tools.r8.ir.optimize.membervaluepropagation.assume.AssumeInfo;
+import com.android.tools.r8.ir.optimize.membervaluepropagation.assume.AssumeInfoLookup;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
-import com.android.tools.r8.shaking.ProguardMemberRule;
import com.android.tools.r8.shaking.ProguardMemberRuleReturnValue;
import com.android.tools.r8.utils.Reporter;
import com.android.tools.r8.utils.StringDiagnostic;
@@ -54,48 +56,17 @@
// Fields for which we have reported warnings to due Proguard configuration rules.
private final Set<DexField> warnedFields = Sets.newIdentityHashSet();
- private enum RuleType {
- NONE,
- ASSUME_NO_SIDE_EFFECTS,
- ASSUME_VALUES
- }
-
- private static class ProguardMemberRuleLookup {
-
- final RuleType type;
- final ProguardMemberRule rule;
-
- ProguardMemberRuleLookup(RuleType type, ProguardMemberRule rule) {
- this.type = type;
- this.rule = rule;
- }
-
- @Override
- public boolean equals(Object other) {
- if (!(other instanceof ProguardMemberRuleLookup)) {
- return false;
- }
- ProguardMemberRuleLookup otherLookup = (ProguardMemberRuleLookup) other;
- return type == otherLookup.type && rule == otherLookup.rule;
- }
-
- @Override
- public int hashCode() {
- return type.ordinal() * 31 + rule.hashCode();
- }
- }
-
public MemberValuePropagation(AppView<AppInfoWithLiveness> appView) {
this.appView = appView;
this.reporter = appView.options().reporter;
}
- private boolean mayPropagateValueFor(DexEncodedField field) {
- if (field.isProgramField(appView)) {
- return appView.appInfo().mayPropagateValueFor(field.field);
+ private boolean mayPropagateValueFor(DexClassAndField field) {
+ if (field.isProgramField()) {
+ return appView.appInfo().mayPropagateValueFor(field.getReference());
}
- return appView.appInfo().assumedValues.containsKey(field.field)
- || appView.appInfo().noSideEffects.containsKey(field.field);
+ return appView.appInfo().assumedValues.containsKey(field.getReference())
+ || appView.appInfo().noSideEffects.containsKey(field.getReference());
}
private boolean mayPropagateValueFor(DexClassAndMethod method) {
@@ -106,36 +77,25 @@
|| appView.appInfo().noSideEffects.containsKey(method.getReference());
}
- private ProguardMemberRuleLookup lookupMemberRule(DexClassAndMethod method) {
- return method != null ? lookupMemberRule(method.getDefinition()) : null;
- }
-
- private ProguardMemberRuleLookup lookupMemberRule(DexDefinition definition) {
- if (definition == null) {
- return null;
- }
- DexReference reference = definition.getReference();
- ProguardMemberRule rule = appView.appInfo().noSideEffects.get(reference);
- if (rule != null) {
- return new ProguardMemberRuleLookup(RuleType.ASSUME_NO_SIDE_EFFECTS, rule);
- }
- rule = appView.appInfo().assumedValues.get(reference);
- if (rule != null) {
- return new ProguardMemberRuleLookup(RuleType.ASSUME_VALUES, rule);
- }
- return null;
- }
-
- private Instruction constantReplacementFromProguardRule(
- ProguardMemberRule rule, IRCode code, Instruction instruction) {
- if (rule == null || !rule.hasReturnValue()) {
+ private Instruction createReplacementFromAssumeInfo(
+ AssumeInfo assumeInfo, IRCode code, Instruction instruction) {
+ if (!assumeInfo.hasReturnInfo()) {
return null;
}
- ProguardMemberRuleReturnValue returnValueRule = rule.getReturnValue();
+ ProguardMemberRuleReturnValue returnValueRule = assumeInfo.getReturnInfo();
// Check if this value can be assumed constant.
if (returnValueRule.isSingleValue()) {
+ if (instruction.getOutType().isReferenceType()) {
+ if (returnValueRule.getSingleValue() == 0) {
+ return appView
+ .abstractValueFactory()
+ .createNullValue()
+ .createMaterializingInstruction(appView, code, instruction);
+ }
+ return null;
+ }
return appView.abstractValueFactory()
.createSingleNumberValue(returnValueRule.getSingleValue())
.createMaterializingInstruction(appView, code, instruction);
@@ -177,31 +137,33 @@
return null;
}
- private void setValueRangeFromProguardRule(ProguardMemberRule rule, Value value) {
- if (rule.hasReturnValue() && rule.getReturnValue().isValueRange()) {
- assert !rule.getReturnValue().isSingleValue();
- value.setValueRange(rule.getReturnValue().getValueRange());
+ private void setValueRangeFromAssumeInfo(AssumeInfo assumeInfo, Value value) {
+ if (assumeInfo.hasReturnInfo() && assumeInfo.getReturnInfo().isValueRange()) {
+ assert !assumeInfo.getReturnInfo().isSingleValue();
+ value.setValueRange(assumeInfo.getReturnInfo().getValueRange());
}
}
- private boolean tryConstantReplacementFromProguard(
+ private boolean applyAssumeInfoIfPossible(
IRCode code,
Set<Value> affectedValues,
ListIterator<BasicBlock> blocks,
InstructionListIterator iterator,
Instruction current,
- ProguardMemberRuleLookup lookup) {
- Instruction replacement = constantReplacementFromProguardRule(lookup.rule, code, current);
+ AssumeInfo assumeInfo) {
+ Instruction replacement = createReplacementFromAssumeInfo(assumeInfo, code, current);
if (replacement == null) {
// Check to see if a value range can be assumed.
- setValueRangeFromProguardRule(lookup.rule, current.outValue());
+ if (current.getOutType().isPrimitiveType()) {
+ setValueRangeFromAssumeInfo(assumeInfo, current.outValue());
+ }
return false;
}
affectedValues.addAll(current.outValue().affectedValues());
- if (lookup.type == RuleType.ASSUME_NO_SIDE_EFFECTS) {
+ if (assumeInfo.isAssumeNoSideEffects()) {
iterator.replaceCurrentInstruction(replacement);
} else {
- assert lookup.type == RuleType.ASSUME_VALUES;
+ assert assumeInfo.isAssumeValues();
BasicBlock block = current.getBlock();
Position position = current.getPosition();
if (current.hasOutValue()) {
@@ -240,43 +202,33 @@
return;
}
- DexClassAndMethod singleTarget = invoke.lookupSingleTarget(appView, context);
- ProguardMemberRuleLookup lookup = lookupMemberRule(singleTarget);
- if (lookup == null) {
- // -assumenosideeffects rules are applied to upward visible and overriding methods, but only
- // references that have actual definitions are marked by the root set builder. So, here, we
- // try again with a resolved target, not the direct definition, which may not exist.
- DexEncodedMethod resolutionTarget =
- appView.appInfo().unsafeResolveMethodDueToDexFormat(invokedMethod).getSingleTarget();
- lookup = lookupMemberRule(resolutionTarget);
- }
-
- if (lookup != null) {
- // Check to see if a constant value can be assumed.
- // But, if the current matched rule is -assumenosideeffects without the return value, it won't
- // be transformed into a replacement instruction. Check if there is -assumevalues rule bound
- // to the target.
- if (singleTarget != null
- && lookup.type == RuleType.ASSUME_NO_SIDE_EFFECTS
- && !lookup.rule.hasReturnValue()) {
- ProguardMemberRule rule = appView.appInfo().assumedValues.get(singleTarget.getReference());
- if (rule != null) {
- lookup = new ProguardMemberRuleLookup(RuleType.ASSUME_VALUES, rule);
- }
- }
- if (tryConstantReplacementFromProguard(
- code, affectedValues, blocks, iterator, invoke, lookup)) {
- return;
- }
- }
-
- // No Proguard rule could replace the instruction check for knowledge about the return value.
- if (singleTarget == null || !mayPropagateValueFor(singleTarget)) {
+ SingleResolutionResult resolutionResult =
+ appView.appInfo().unsafeResolveMethodDueToDexFormat(invokedMethod).asSingleResolution();
+ if (resolutionResult == null) {
return;
}
- AbstractValue abstractReturnValue =
- singleTarget.getDefinition().getOptimizationInfo().getAbstractReturnValue();
+ DexClassAndMethod singleTarget = invoke.lookupSingleTarget(appView, context);
+ AssumeInfo lookup = AssumeInfoLookup.lookupAssumeInfo(appView, resolutionResult, singleTarget);
+ if (lookup != null
+ && applyAssumeInfoIfPossible(code, affectedValues, blocks, iterator, invoke, lookup)) {
+ return;
+ }
+
+ // No Proguard rule could replace the instruction check for knowledge about the return value.
+ if (singleTarget != null && !mayPropagateValueFor(singleTarget)) {
+ return;
+ }
+
+ AbstractValue abstractReturnValue;
+ if (invokedMethod.getReturnType().isAlwaysNull(appView)) {
+ abstractReturnValue = appView.abstractValueFactory().createNullValue();
+ } else if (singleTarget != null) {
+ abstractReturnValue =
+ singleTarget.getDefinition().getOptimizationInfo().getAbstractReturnValue();
+ } else {
+ abstractReturnValue = UnknownValue.getInstance();
+ }
if (abstractReturnValue.isSingleValue()) {
SingleValue singleReturnValue = abstractReturnValue.asSingleValue();
@@ -293,7 +245,7 @@
if (invoke.isInvokeMethodWithReceiver()) {
iterator.replaceCurrentInstructionByNullCheckIfPossible(appView, context);
- } else if (invoke.isInvokeStatic()) {
+ } else if (invoke.isInvokeStatic() && singleTarget != null) {
iterator.replaceCurrentInstructionByInitClassIfPossible(
appView, code, singleTarget.getHolderType());
}
@@ -308,7 +260,10 @@
} else {
iterator.add(replacement);
}
- singleTarget.getDefinition().getMutableOptimizationInfo().markAsPropagated();
+
+ if (singleTarget != null) {
+ singleTarget.getDefinition().getMutableOptimizationInfo().markAsPropagated();
+ }
}
}
}
@@ -322,8 +277,9 @@
DexField field = current.getField();
// TODO(b/123857022): Should be able to use definitionFor().
- DexEncodedField target = appView.appInfo().resolveField(field).getResolvedField();
- if (target == null) {
+ SuccessfulFieldResolutionResult resolutionResult =
+ appView.appInfo().resolveField(field).asSuccessfulResolution();
+ if (resolutionResult == null) {
boolean replaceCurrentInstructionWithConstNull =
appView.withGeneratedExtensionRegistryShrinker(
shrinker -> shrinker.wasRemoved(field), false);
@@ -333,7 +289,9 @@
return;
}
- if (target.isStatic() != current.isStaticGet()) {
+ DexClassAndField target = resolutionResult.getResolutionPair();
+ DexEncodedField definition = target.getDefinition();
+ if (definition.isStatic() != current.isStaticGet()) {
return;
}
@@ -342,33 +300,35 @@
}
// Check if there is a Proguard configuration rule that specifies the value of the field.
- ProguardMemberRuleLookup lookup = lookupMemberRule(target);
+ AssumeInfo lookup = AssumeInfoLookup.lookupAssumeInfo(appView, target);
if (lookup != null
- && tryConstantReplacementFromProguard(
- code, affectedValues, blocks, iterator, current, lookup)) {
+ && applyAssumeInfoIfPossible(code, affectedValues, blocks, iterator, current, lookup)) {
return;
}
AbstractValue abstractValue;
if (field.getType().isAlwaysNull(appView)) {
abstractValue = appView.abstractValueFactory().createSingleNumberValue(0);
- } else if (appView.appInfo().isFieldWrittenByFieldPutInstruction(target)) {
- abstractValue = target.getOptimizationInfo().getAbstractValue();
- if (abstractValue.isUnknown() && !target.isStatic()) {
+ } else if (appView.appInfo().isFieldWrittenByFieldPutInstruction(definition)) {
+ abstractValue = definition.getOptimizationInfo().getAbstractValue();
+ if (abstractValue.isUnknown() && !definition.isStatic()) {
AbstractValue abstractReceiverValue =
current.asInstanceGet().object().getAbstractValue(appView, code.context());
if (abstractReceiverValue.isSingleFieldValue()) {
abstractValue =
- abstractReceiverValue.asSingleFieldValue().getState().getAbstractFieldValue(target);
+ abstractReceiverValue
+ .asSingleFieldValue()
+ .getState()
+ .getAbstractFieldValue(definition);
}
}
- } else if (target.isStatic()) {
+ } else if (definition.isStatic()) {
// This is guaranteed to read the static value of the field.
- abstractValue = target.getStaticValue().toAbstractValue(appView.abstractValueFactory());
+ abstractValue = definition.getStaticValue().toAbstractValue(appView.abstractValueFactory());
// Verify that the optimization info is consistent with the static value.
- assert target.getOptimizationInfo().getAbstractValue().isUnknown()
- || !target.hasExplicitStaticValue()
- || abstractValue == target.getOptimizationInfo().getAbstractValue();
+ assert definition.getOptimizationInfo().getAbstractValue().isUnknown()
+ || !definition.hasExplicitStaticValue()
+ || abstractValue == definition.getOptimizationInfo().getAbstractValue();
} else {
// This is guaranteed to read the default value of the field.
abstractValue = appView.abstractValueFactory().createSingleNumberValue(0);
@@ -411,7 +371,8 @@
} else {
iterator.add(replacement);
}
- feedback.markFieldAsPropagated(target);
+
+ feedback.markFieldAsPropagated(definition);
}
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/NestReducer.java b/src/main/java/com/android/tools/r8/ir/optimize/NestReducer.java
index 6fa08c8..e20e28c 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/NestReducer.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/NestReducer.java
@@ -4,131 +4,102 @@
package com.android.tools.r8.ir.optimize;
+import static com.android.tools.r8.graph.DexProgramClass.asProgramClassOrNull;
+
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.DexEncodedMember;
import com.android.tools.r8.graph.DexProgramClass;
-import com.android.tools.r8.graph.DexType;
-import com.android.tools.r8.graph.NestMemberClassAttribute;
+import com.android.tools.r8.shaking.AppInfoWithLiveness;
+import com.android.tools.r8.utils.BooleanBox;
+import com.android.tools.r8.utils.IterableUtils;
import com.android.tools.r8.utils.ThreadUtils;
+import com.android.tools.r8.utils.Timing;
import com.google.common.collect.Sets;
-import java.util.ArrayList;
-import java.util.Iterator;
-import java.util.List;
import java.util.Set;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Future;
-// This pass
-// - cleans the nests: it removes missing nest host/members from the input and
-// report them (warning or error).
-// - clears nests which do not use nest based access control to allow other
-// optimizations such as class merging to perform better.
+/**
+ * This pass:
+ *
+ * <ul>
+ * <li>cleans the nests: it removes missing nest host/members from the input,
+ * <li>clears nests which do not use nest based access control to allow other optimizations such
+ * as class merging to perform better.
+ * </ul>
+ */
public class NestReducer {
- private AppView<?> appView;
+ private AppView<AppInfoWithLiveness> appView;
- public NestReducer(AppView<?> appView) {
+ public NestReducer(AppView<AppInfoWithLiveness> appView) {
this.appView = appView;
}
- private DexClass definitionFor(DexType type) {
- assert appView.graphLens().lookupType(type) == type;
- return appView.definitionFor(appView.graphLens().lookupType(type));
+ public void run(ExecutorService executorService, Timing timing) throws ExecutionException {
+ timing.begin("NestReduction");
+ if (appView.options().shouldDesugarNests()) {
+ removeNests();
+ } else {
+ reduceNests(executorService);
+ }
+ timing.end();
}
- public void run(ExecutorService executorService) throws ExecutionException {
- Set<DexType> nestHosts = Sets.newIdentityHashSet();
- List<Future<?>> futures = new ArrayList<>();
- // It is possible that a nest member is on the program path but its nest host
- // is only in the class path.
- // Nests are therefore computed the first time a nest member is met, host or not.
- // The computedNestHosts list is there to avoid processing multiple times the same nest.
+ private void removeNests() {
for (DexProgramClass clazz : appView.appInfo().classes()) {
- DexType hostType = clazz.getNestHost();
- if (hostType != null && !nestHosts.contains(hostType)) {
- nestHosts.add(hostType);
- futures.add(
- executorService.submit(
- () -> {
- processNestFrom(clazz);
- return null; // we want a Callable not a Runnable to be able to throw
- }));
- }
- }
- ThreadUtils.awaitFutures(futures);
- }
-
- private void processNestFrom(DexClass clazz) {
- DexClass nestHost = definitionFor(clazz.getNestHost());
- if (nestHost == null) {
- reportMissingNestHost(clazz);
- clazz.clearNestHost();
- return;
- }
- boolean hasPrivateMembers = hasPrivateMembers(nestHost);
- Iterator<NestMemberClassAttribute> iterator =
- nestHost.getNestMembersClassAttributes().iterator();
- boolean reported = false;
- while (iterator.hasNext()) {
- DexClass member = definitionFor(iterator.next().getNestMember());
- if (member == null) {
- if (!reported) {
- reported = true;
- reportIncompleteNest(nestHost);
+ if (clazz.isInANest()) {
+ if (clazz.isNestHost()) {
+ clazz.clearNestMembers();
+ } else {
+ clazz.clearNestHost();
}
- iterator.remove();
- } else {
- hasPrivateMembers = hasPrivateMembers || hasPrivateMembers(member);
}
}
- if (!hasPrivateMembers && appView.options().enableNestReduction) {
- clearNestAttributes(nestHost);
- }
}
- private void reportMissingNestHost(DexClass clazz) {
- if (appView.options().ignoreMissingClasses) {
- appView.options().warningMissingClassMissingNestHost(clazz);
- } else {
- appView.options().errorMissingClassMissingNestHost(clazz);
- }
- }
-
- private void reportIncompleteNest(DexClass nestHost) {
- List<DexType> nest = new ArrayList<>(nestHost.getNestMembersClassAttributes().size() + 1);
- for (NestMemberClassAttribute attr : nestHost.getNestMembersClassAttributes()) {
- nest.add(attr.getNestMember());
- }
- nest.add(nestHost.type);
- if (appView.options().ignoreMissingClasses) {
- appView.options().warningMissingClassIncompleteNest(nest, appView);
- } else {
- appView.options().errorMissingClassIncompleteNest(nest, appView);
- }
- }
-
- private void clearNestAttributes(DexClass nestHost) {
- nestHost.getNestMembersClassAttributes().clear();
- for (NestMemberClassAttribute attr : nestHost.getNestMembersClassAttributes()) {
- DexClass member = appView.definitionFor(appView.graphLens().lookupType(attr.getNestMember()));
- member.clearNestHost();
- }
- }
-
- private boolean hasPrivateMembers(DexClass clazz) {
- for (DexEncodedMethod method : clazz.methods()) {
- if (method.accessFlags.isPrivate()) {
- return true;
+ private void reduceNests(ExecutorService executorService) throws ExecutionException {
+ Set<DexProgramClass> nestHosts = Sets.newIdentityHashSet();
+ Set<DexProgramClass> nestMembers = Sets.newIdentityHashSet();
+ for (DexProgramClass clazz : appView.appInfo().classes()) {
+ if (clazz.isInANest()) {
+ if (clazz.isNestHost()) {
+ nestHosts.add(clazz);
+ } else {
+ nestMembers.add(clazz);
+ }
}
}
- for (DexEncodedField field : clazz.fields()) {
- if (field.accessFlags.isPrivate()) {
- return true;
- }
+ ThreadUtils.processItems(nestHosts, this::processNestHost, executorService);
+ ThreadUtils.processItems(nestMembers, this::processNestMember, executorService);
+ }
+
+ private void processNestHost(DexProgramClass clazz) {
+ BooleanBox nestHasPrivateMembers =
+ new BooleanBox(IterableUtils.hasNext(clazz.members(DexEncodedMember::isPrivate)));
+ clazz
+ .getNestMembersClassAttributes()
+ .removeIf(
+ attribute -> {
+ DexProgramClass member =
+ asProgramClassOrNull(appView.definitionFor(attribute.getNestMember(), clazz));
+ if (member == null) {
+ return true;
+ }
+ nestHasPrivateMembers.computeIfNotSet(
+ () -> IterableUtils.hasNext(member.members(DexEncodedMember::isPrivate)));
+ return false;
+ });
+ if (nestHasPrivateMembers.isFalse() && appView.options().enableNestReduction) {
+ clazz.getNestMembersClassAttributes().clear();
}
- return false;
+ }
+
+ private void processNestMember(DexProgramClass clazz) {
+ DexProgramClass hostClass =
+ asProgramClassOrNull(appView.definitionFor(clazz.getNestHost(), clazz));
+ if (hostClass == null || !hostClass.isNestHost()) {
+ clazz.clearNestHost();
+ }
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/Outliner.java b/src/main/java/com/android/tools/r8/ir/optimize/Outliner.java
index f673395..11b4ddf 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/Outliner.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/Outliner.java
@@ -955,7 +955,7 @@
return true;
}
if (arrayBaseType.isClassType()) {
- return arrayBaseType.asClassType().getInterfaces().size() == 0;
+ return arrayBaseType.asClassType().getInterfaces().isEmpty();
}
return false;
}
@@ -972,8 +972,8 @@
// have a common super interface nor are they implemented by a common superclass so the
// argument type of the outline will be java.lang.Object.
if (valueClassType.getClassType() == objectType
- && valueClassType.getInterfaces().size() == 1) {
- return valueClassType.getInterfaces().iterator().next();
+ && valueClassType.getInterfaces().hasSingleKnownInterface()) {
+ return valueClassType.getInterfaces().getSingleKnownInterface();
} else {
return valueClassType.getClassType();
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/ServiceLoaderRewriter.java b/src/main/java/com/android/tools/r8/ir/optimize/ServiceLoaderRewriter.java
index 1694a7b..efc440b 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/ServiceLoaderRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/ServiceLoaderRewriter.java
@@ -4,25 +4,15 @@
package com.android.tools.r8.ir.optimize;
-import com.android.tools.r8.dex.Constants;
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.DexClass;
-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.DexProgramClass.ChecksumSupplier;
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.GenericSignature.ClassSignature;
-import com.android.tools.r8.graph.GenericSignature.MethodTypeSignature;
import com.android.tools.r8.graph.MethodAccessFlags;
-import com.android.tools.r8.graph.MethodCollection;
-import com.android.tools.r8.graph.ParameterAnnotationsList;
+import com.android.tools.r8.graph.ProgramMethod;
import com.android.tools.r8.ir.code.ConstClass;
import com.android.tools.r8.ir.code.IRCode;
import com.android.tools.r8.ir.code.Instruction;
@@ -32,15 +22,13 @@
import com.android.tools.r8.ir.code.Value;
import com.android.tools.r8.ir.conversion.MethodProcessingId;
import com.android.tools.r8.ir.desugar.ServiceLoaderSourceCode;
-import com.android.tools.r8.origin.SynthesizedOrigin;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
+import com.android.tools.r8.synthesis.SyntheticNaming.SyntheticKind;
import com.google.common.collect.ImmutableList;
import java.util.ArrayList;
-import java.util.Collections;
import java.util.IdentityHashMap;
import java.util.List;
import java.util.Map;
-import java.util.concurrent.atomic.AtomicReference;
/**
* ServiceLoaderRewriter will attempt to rewrite calls on the form of: ServiceLoader.load(X.class,
@@ -71,19 +59,15 @@
*/
public class ServiceLoaderRewriter {
- public static final String SERVICE_LOADER_CLASS_NAME = "$$ServiceLoaderMethods";
- private static final String SERVICE_LOADER_METHOD_PREFIX_NAME = "$load";
-
- private AtomicReference<DexProgramClass> synthesizedClass = new AtomicReference<>();
-
private final AppView<AppInfoWithLiveness> appView;
+ private final List<ProgramMethod> serviceLoadMethods = new ArrayList<>();
public ServiceLoaderRewriter(AppView<AppInfoWithLiveness> appView) {
this.appView = appView;
}
- public DexProgramClass getSynthesizedClass() {
- return synthesizedClass.get();
+ public List<ProgramMethod> getServiceLoadMethods() {
+ return serviceLoadMethods;
}
public void rewrite(IRCode code, MethodProcessingId methodProcessingId) {
@@ -186,7 +170,7 @@
constClass.getValue(),
service -> {
DexEncodedMethod addedMethod =
- createSynthesizedMethod(service, classes, methodProcessingId);
+ createSynthesizedMethod(service, classes, methodProcessingId, code.context());
if (appView.options().isGeneratingClassFiles()) {
addedMethod.upgradeClassFileVersion(code.method().getClassFileVersion());
}
@@ -199,75 +183,31 @@
}
private DexEncodedMethod createSynthesizedMethod(
- DexType serviceType, List<DexClass> classes, MethodProcessingId methodProcessingId) {
- MethodCollection methodCollection = getOrSetSynthesizedClass().getMethodCollection();
- String methodNamePrefix = SERVICE_LOADER_METHOD_PREFIX_NAME + "$";
+ DexType serviceType,
+ List<DexClass> classes,
+ MethodProcessingId methodProcessingId,
+ ProgramMethod context) {
DexProto proto = appView.dexItemFactory().createProto(appView.dexItemFactory().iteratorType);
- synchronized (methodCollection) {
- DexMethod methodReference;
- do {
- methodReference =
- appView
- .dexItemFactory()
- .createMethod(
- appView.dexItemFactory().serviceLoaderRewrittenClassType,
- proto,
- methodNamePrefix + methodProcessingId.getAndIncrementId());
- } while (methodCollection.getMethod(methodReference) != null);
- DexEncodedMethod method =
- new DexEncodedMethod(
- methodReference,
- MethodAccessFlags.fromSharedAccessFlags(
- Constants.ACC_PUBLIC | Constants.ACC_STATIC, false),
- MethodTypeSignature.noSignature(),
- DexAnnotationSet.empty(),
- ParameterAnnotationsList.empty(),
- ServiceLoaderSourceCode.generate(serviceType, classes, appView.dexItemFactory()),
- true);
- methodCollection.addDirectMethod(method);
- return method;
+ ProgramMethod method =
+ appView
+ .getSyntheticItems()
+ .createMethod(
+ SyntheticKind.SERVICE_LOADER,
+ context,
+ appView.dexItemFactory(),
+ builder ->
+ builder
+ .setAccessFlags(MethodAccessFlags.createPublicStaticSynthetic())
+ .setProto(proto)
+ .setCode(
+ m ->
+ ServiceLoaderSourceCode.generate(
+ serviceType, classes, appView.dexItemFactory())),
+ methodProcessingId);
+ synchronized (serviceLoadMethods) {
+ serviceLoadMethods.add(method);
}
- }
-
- private DexProgramClass getOrSetSynthesizedClass() {
- if (synthesizedClass.get() != null) {
- return synthesizedClass.get();
- }
- assert !appView.options().encodeChecksums;
- ChecksumSupplier checksumSupplier = DexProgramClass::invalidChecksumRequest;
- DexProgramClass clazz =
- synthesizedClass.updateAndGet(
- existingClass -> {
- if (existingClass != null) {
- return existingClass;
- }
- DexProgramClass newClass =
- new DexProgramClass(
- appView.dexItemFactory().serviceLoaderRewrittenClassType,
- null,
- new SynthesizedOrigin("Service Loader desugaring", getClass()),
- ClassAccessFlags.fromDexAccessFlags(
- Constants.ACC_FINAL | Constants.ACC_SYNTHETIC | Constants.ACC_PUBLIC),
- appView.dexItemFactory().objectType,
- DexTypeList.empty(),
- appView.dexItemFactory().createString("ServiceLoader"),
- null,
- Collections.emptyList(),
- null,
- Collections.emptyList(),
- ClassSignature.noSignature(),
- DexAnnotationSet.empty(),
- DexEncodedField.EMPTY_ARRAY, // Static fields.
- DexEncodedField.EMPTY_ARRAY, // Instance fields.
- DexEncodedMethod.EMPTY_ARRAY,
- DexEncodedMethod.EMPTY_ARRAY, // Virtual methods.
- appView.dexItemFactory().getSkipNameValidationForTesting(),
- checksumSupplier);
- newClass.getMethodCollection().useSortedBacking();
- return newClass;
- });
- assert clazz != null;
- return clazz;
+ return method.getDefinition();
}
/**
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/UtilityMethodsForCodeOptimizations.java b/src/main/java/com/android/tools/r8/ir/optimize/UtilityMethodsForCodeOptimizations.java
index 1b55f13..9e360b4 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/UtilityMethodsForCodeOptimizations.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/UtilityMethodsForCodeOptimizations.java
@@ -4,6 +4,7 @@
package com.android.tools.r8.ir.optimize;
+import com.android.tools.r8.cf.CfVersion;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.CfCode;
import com.android.tools.r8.graph.DexItemFactory;
@@ -15,6 +16,7 @@
import com.android.tools.r8.ir.conversion.MethodProcessor;
import com.android.tools.r8.ir.optimize.templates.CfUtilityMethodsForCodeOptimizations;
import com.android.tools.r8.synthesis.SyntheticItems;
+import com.android.tools.r8.synthesis.SyntheticNaming;
import com.android.tools.r8.utils.InternalOptions;
public class UtilityMethodsForCodeOptimizations {
@@ -22,22 +24,20 @@
public static UtilityMethodForCodeOptimizations synthesizeToStringIfNotNullMethod(
AppView<?> appView, ProgramMethod context, MethodProcessingId methodProcessingId) {
InternalOptions options = appView.options();
- if (options.isGeneratingClassFiles()) {
- // TODO(b/172194277): Allow synthetics when generating CF.
- return null;
- }
DexItemFactory dexItemFactory = appView.dexItemFactory();
DexProto proto = dexItemFactory.createProto(dexItemFactory.voidType, dexItemFactory.objectType);
SyntheticItems syntheticItems = appView.getSyntheticItems();
ProgramMethod syntheticMethod =
syntheticItems.createMethod(
+ SyntheticNaming.SyntheticKind.TO_STRING_IF_NOT_NULL,
context,
dexItemFactory,
builder ->
builder
- .setProto(proto)
.setAccessFlags(MethodAccessFlags.createPublicStaticSynthetic())
- .setCode(method -> getToStringIfNotNullCodeTemplate(method, options)),
+ .setClassFileVersion(CfVersion.V1_8)
+ .setCode(method -> getToStringIfNotNullCodeTemplate(method, options))
+ .setProto(proto),
methodProcessingId);
return new UtilityMethodForCodeOptimizations(syntheticMethod);
}
@@ -51,23 +51,21 @@
public static UtilityMethodForCodeOptimizations synthesizeThrowClassCastExceptionIfNotNullMethod(
AppView<?> appView, ProgramMethod context, MethodProcessingId methodProcessingId) {
InternalOptions options = appView.options();
- if (options.isGeneratingClassFiles()) {
- // TODO(b/172194277): Allow synthetics when generating CF.
- return null;
- }
DexItemFactory dexItemFactory = appView.dexItemFactory();
DexProto proto = dexItemFactory.createProto(dexItemFactory.voidType, dexItemFactory.objectType);
SyntheticItems syntheticItems = appView.getSyntheticItems();
ProgramMethod syntheticMethod =
syntheticItems.createMethod(
+ SyntheticNaming.SyntheticKind.THROW_CCE_IF_NOT_NULL,
context,
dexItemFactory,
builder ->
builder
- .setProto(proto)
.setAccessFlags(MethodAccessFlags.createPublicStaticSynthetic())
+ .setClassFileVersion(CfVersion.V1_8)
.setCode(
- method -> getThrowClassCastExceptionIfNotNullCodeTemplate(method, options)),
+ method -> getThrowClassCastExceptionIfNotNullCodeTemplate(method, options))
+ .setProto(proto),
methodProcessingId);
return new UtilityMethodForCodeOptimizations(syntheticMethod);
}
@@ -79,6 +77,62 @@
options, method);
}
+ public static UtilityMethodForCodeOptimizations synthesizeThrowIncompatibleClassChangeErrorMethod(
+ AppView<?> appView, ProgramMethod context, MethodProcessingId methodProcessingId) {
+ InternalOptions options = appView.options();
+ DexItemFactory dexItemFactory = appView.dexItemFactory();
+ DexProto proto = dexItemFactory.createProto(dexItemFactory.icceType);
+ SyntheticItems syntheticItems = appView.getSyntheticItems();
+ ProgramMethod syntheticMethod =
+ syntheticItems.createMethod(
+ SyntheticNaming.SyntheticKind.THROW_ICCE,
+ context,
+ dexItemFactory,
+ builder ->
+ builder
+ .setAccessFlags(MethodAccessFlags.createPublicStaticSynthetic())
+ .setClassFileVersion(CfVersion.V1_8)
+ .setCode(
+ method -> getThrowIncompatibleClassChangeErrorCodeTemplate(method, options))
+ .setProto(proto),
+ methodProcessingId);
+ return new UtilityMethodForCodeOptimizations(syntheticMethod);
+ }
+
+ private static CfCode getThrowIncompatibleClassChangeErrorCodeTemplate(
+ DexMethod method, InternalOptions options) {
+ return CfUtilityMethodsForCodeOptimizations
+ .CfUtilityMethodsForCodeOptimizationsTemplates_throwIncompatibleClassChangeError(
+ options, method);
+ }
+
+ public static UtilityMethodForCodeOptimizations synthesizeThrowNoSuchMethodErrorMethod(
+ AppView<?> appView, ProgramMethod context, MethodProcessingId methodProcessingId) {
+ InternalOptions options = appView.options();
+ DexItemFactory dexItemFactory = appView.dexItemFactory();
+ DexProto proto = dexItemFactory.createProto(dexItemFactory.noSuchMethodErrorType);
+ SyntheticItems syntheticItems = appView.getSyntheticItems();
+ ProgramMethod syntheticMethod =
+ syntheticItems.createMethod(
+ SyntheticNaming.SyntheticKind.THROW_NSME,
+ context,
+ dexItemFactory,
+ builder ->
+ builder
+ .setAccessFlags(MethodAccessFlags.createPublicStaticSynthetic())
+ .setClassFileVersion(CfVersion.V1_8)
+ .setCode(method -> getThrowNoSuchMethodErrorCodeTemplate(method, options))
+ .setProto(proto),
+ methodProcessingId);
+ return new UtilityMethodForCodeOptimizations(syntheticMethod);
+ }
+
+ private static CfCode getThrowNoSuchMethodErrorCodeTemplate(
+ DexMethod method, InternalOptions options) {
+ return CfUtilityMethodsForCodeOptimizations
+ .CfUtilityMethodsForCodeOptimizationsTemplates_throwNoSuchMethodError(options, method);
+ }
+
public static class UtilityMethodForCodeOptimizations {
private final ProgramMethod method;
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/ClassInliner.java b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/ClassInliner.java
index 5d79d0e..13865a0 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/ClassInliner.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/ClassInliner.java
@@ -223,7 +223,7 @@
// methods (which is bad for memory), or we would need to analyze the called methods
// before inlining them. The latter could be good solution, since we are going to build IR
// for the methods that need to be inlined anyway.
- assert appView.options().testing.allowClassInlinerGracefulExit;
+ assert false;
anyInlinedMethods = true;
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxer.java b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxer.java
index 166022e..cf0ecb5 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxer.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxer.java
@@ -491,34 +491,36 @@
// Step 1: We iterate over the field to find direct enum instance information and the values
// fields.
for (DexEncodedField staticField : enumClass.staticFields()) {
- if (EnumUnboxingCandidateAnalysis.isEnumField(staticField, enumClass.type)) {
+ if (factory.enumMembers.isEnumField(staticField, enumClass.type)) {
ObjectState enumState =
enumStaticFieldValues.getObjectStateForPossiblyPinnedField(staticField.field);
- if (enumState != null) {
- OptionalInt optionalOrdinal = getOrdinal(enumState);
- if (!optionalOrdinal.isPresent()) {
- return null;
+ if (enumState == null) {
+ if (!isFinalFieldInitialized(staticField, enumClass)) {
+ continue;
}
- int ordinal = optionalOrdinal.getAsInt();
- unboxedValues.put(staticField.field, ordinalToUnboxedInt(ordinal));
- ordinalToObjectState.put(ordinal, enumState);
- }
- } else if (EnumUnboxingCandidateAnalysis.matchesValuesField(
- staticField, enumClass.type, factory)) {
- AbstractValue valuesValue =
- enumStaticFieldValues.getValuesAbstractValueForPossiblyPinnedField(staticField.field);
- if (valuesValue == null || valuesValue.isZero()) {
- // Unused field
- continue;
- }
- if (valuesValue.isUnknown()) {
+ // Tracking the content of the field yielded either an empty object state, or something
+ // incoherent. We bail out.
return null;
}
- assert valuesValue.isSingleFieldValue();
- ObjectState valuesState = valuesValue.asSingleFieldValue().getState();
- if (!valuesState.isEnumValuesObjectState()) {
+ OptionalInt optionalOrdinal = getOrdinal(enumState);
+ if (!optionalOrdinal.isPresent()) {
return null;
}
+ int ordinal = optionalOrdinal.getAsInt();
+ unboxedValues.put(staticField.field, ordinalToUnboxedInt(ordinal));
+ ordinalToObjectState.put(ordinal, enumState);
+ } else if (factory.enumMembers.isValuesFieldCandidate(staticField, enumClass.type)) {
+ ObjectState valuesState =
+ enumStaticFieldValues.getObjectStateForPossiblyPinnedField(staticField.field);
+ if (valuesState == null) {
+ if (!isFinalFieldInitialized(staticField, enumClass)) {
+ continue;
+ }
+ // We could not track the content of that field, and the field could be a values field.
+ // We conservatively bail out.
+ return null;
+ }
+ assert valuesState.isEnumValuesObjectState();
assert valuesContents == null
|| valuesContents.equals(valuesState.asEnumValuesObjectState());
valuesContents = valuesState.asEnumValuesObjectState();
@@ -564,6 +566,13 @@
valuesContents == null ? EnumData.INVALID_VALUES_SIZE : valuesContents.getEnumValuesSize());
}
+ private boolean isFinalFieldInitialized(DexEncodedField staticField, DexProgramClass enumClass) {
+ assert staticField.isFinal();
+ return appView
+ .appInfo()
+ .isFieldOnlyWrittenInMethodIgnoringPinning(staticField, enumClass.getClassInitializer());
+ }
+
private EnumInstanceFieldData computeEnumFieldData(
DexField instanceField,
DexProgramClass enumClass,
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingRewriter.java b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingRewriter.java
index 6c20c4f..4c7581e 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingRewriter.java
@@ -434,7 +434,7 @@
return factory.createField(
relocator.getNewMemberLocationFor(enumType),
factory.intArrayType,
- factory.enumValuesFieldName + "$field$" + compatibleName(enumType));
+ "$$values$$field$" + compatibleName(enumType));
}
private DexEncodedField computeValuesEncodedField(DexField field) {
@@ -451,7 +451,7 @@
return factory.createMethod(
relocator.getNewMemberLocationFor(enumType),
factory.createProto(factory.intArrayType),
- factory.enumValuesFieldName + "$method$" + compatibleName(enumType));
+ "$$values$$method$" + compatibleName(enumType));
}
private DexEncodedMethod computeValuesEncodedMethod(
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 f2f70ca..10a50ca 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
@@ -120,9 +120,9 @@
}
private DexEncodedMethod fixupEncodedMethod(DexEncodedMethod method) {
- DexProto oldProto = method.proto();
+ DexProto oldProto = method.getProto();
DexProto newProto = fixupProto(oldProto);
- if (newProto == method.proto()) {
+ if (newProto == method.getProto()) {
return method;
}
assert !method.isClassInitializer();
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/MethodOptimizationInfoCollector.java b/src/main/java/com/android/tools/r8/ir/optimize/info/MethodOptimizationInfoCollector.java
index b5dfee6..9868a64 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/info/MethodOptimizationInfoCollector.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/MethodOptimizationInfoCollector.java
@@ -1090,10 +1090,18 @@
} else if (classInitializerSideEffect.canBePostponed()) {
feedback.classInitializerMayBePostponed(method);
} else {
- assert !context.getHolderType().isD8R8SynthesizedLambdaClassType()
- || options.debug
- || appView.appInfo().hasPinnedInstanceInitializer(context.getHolderType())
- || appView.options().horizontalClassMergerOptions().isJavaLambdaMergingEnabled()
+ assert options.debug
+ || appView
+ .getSyntheticItems()
+ .verifySyntheticLambdaProperty(
+ context.getHolder(),
+ lambdaClass ->
+ appView.appInfo().hasPinnedInstanceInitializer(lambdaClass.getType())
+ || appView
+ .options()
+ .horizontalClassMergerOptions()
+ .isJavaLambdaMergingEnabled(),
+ nonLambdaClass -> true)
: "Unexpected observable side effects from lambda `" + context.toSourceString() + "`";
}
return;
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
index 89ffafd..e9ff9e8 100644
--- 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
@@ -50,6 +50,7 @@
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.ProguardConfiguration;
import com.android.tools.r8.utils.SetUtils;
import com.android.tools.r8.utils.StringDiagnostic;
import com.android.tools.r8.utils.ThreadUtils;
@@ -397,20 +398,30 @@
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) {
- if (error.reportable) {
- reporter.info(
- new StringDiagnostic("Unexpected Kotlin lambda structure [" +
- info.clazz.type.toSourceString() + "]: " + error.getMessage())
- );
- }
- invalidateLambda(info.clazz.type);
- }
- })));
+ 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);
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/library/StringBuilderMethodOptimizer.java b/src/main/java/com/android/tools/r8/ir/optimize/library/StringBuilderMethodOptimizer.java
index 99b1bda..d75e060 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/library/StringBuilderMethodOptimizer.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/library/StringBuilderMethodOptimizer.java
@@ -122,16 +122,13 @@
UtilityMethodForCodeOptimizations toStringIfNotNullMethod =
UtilityMethodsForCodeOptimizations.synthesizeToStringIfNotNullMethod(
appView, code.context(), state.methodProcessingId);
- // TODO(b/172194277): Allow synthetics when generating CF.
- if (toStringIfNotNullMethod != null) {
- toStringIfNotNullMethod.optimize(state.methodProcessor);
- InvokeStatic replacement =
- InvokeStatic.builder()
- .setMethod(toStringIfNotNullMethod.getMethod())
- .setSingleArgument(object)
- .build();
- instructionIterator.replaceCurrentInstruction(replacement);
- }
+ toStringIfNotNullMethod.optimize(state.methodProcessor);
+ InvokeStatic replacement =
+ InvokeStatic.builder()
+ .setMethod(toStringIfNotNullMethod.getMethod())
+ .setSingleArgument(object)
+ .build();
+ instructionIterator.replaceCurrentInstruction(replacement);
}
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/membervaluepropagation/assume/AssumeInfo.java b/src/main/java/com/android/tools/r8/ir/optimize/membervaluepropagation/assume/AssumeInfo.java
new file mode 100644
index 0000000..a704abb
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/optimize/membervaluepropagation/assume/AssumeInfo.java
@@ -0,0 +1,67 @@
+// 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.membervaluepropagation.assume;
+
+import com.android.tools.r8.shaking.ProguardMemberRule;
+import com.android.tools.r8.shaking.ProguardMemberRuleReturnValue;
+
+public class AssumeInfo {
+
+ public enum AssumeType {
+ ASSUME_NO_SIDE_EFFECTS,
+ ASSUME_VALUES;
+
+ AssumeType meet(AssumeType type) {
+ return this == ASSUME_NO_SIDE_EFFECTS || type == ASSUME_NO_SIDE_EFFECTS
+ ? ASSUME_NO_SIDE_EFFECTS
+ : ASSUME_VALUES;
+ }
+ }
+
+ private final AssumeType type;
+ private final ProguardMemberRule rule;
+
+ public AssumeInfo(AssumeType type, ProguardMemberRule rule) {
+ this.type = type;
+ this.rule = rule;
+ }
+
+ public boolean hasReturnInfo() {
+ return rule.hasReturnValue();
+ }
+
+ public ProguardMemberRuleReturnValue getReturnInfo() {
+ return rule.getReturnValue();
+ }
+
+ public boolean isAssumeNoSideEffects() {
+ return type == AssumeType.ASSUME_NO_SIDE_EFFECTS;
+ }
+
+ public boolean isAssumeValues() {
+ return type == AssumeType.ASSUME_VALUES;
+ }
+
+ public AssumeInfo meet(AssumeInfo lookup) {
+ return new AssumeInfo(type.meet(lookup.type), rule.hasReturnValue() ? rule : lookup.rule);
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (other == null) {
+ return false;
+ }
+ if (!(other instanceof AssumeInfo)) {
+ return false;
+ }
+ AssumeInfo assumeInfo = (AssumeInfo) other;
+ return type == assumeInfo.type && rule == assumeInfo.rule;
+ }
+
+ @Override
+ public int hashCode() {
+ return type.ordinal() * 31 + rule.hashCode();
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/membervaluepropagation/assume/AssumeInfoLookup.java b/src/main/java/com/android/tools/r8/ir/optimize/membervaluepropagation/assume/AssumeInfoLookup.java
new file mode 100644
index 0000000..5ddba94
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/optimize/membervaluepropagation/assume/AssumeInfoLookup.java
@@ -0,0 +1,51 @@
+// 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.membervaluepropagation.assume;
+
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexClassAndMember;
+import com.android.tools.r8.graph.DexClassAndMethod;
+import com.android.tools.r8.graph.DexMember;
+import com.android.tools.r8.graph.ResolutionResult.SingleResolutionResult;
+import com.android.tools.r8.ir.optimize.membervaluepropagation.assume.AssumeInfo.AssumeType;
+import com.android.tools.r8.shaking.AppInfoWithLiveness;
+import com.android.tools.r8.shaking.ProguardMemberRule;
+
+public class AssumeInfoLookup {
+
+ public static AssumeInfo lookupAssumeInfo(
+ AppView<AppInfoWithLiveness> appView,
+ SingleResolutionResult resolutionResult,
+ DexClassAndMethod singleTarget) {
+ AssumeInfo resolutionLookup = lookupAssumeInfo(appView, resolutionResult.getResolutionPair());
+ if (resolutionLookup == null) {
+ return singleTarget != null ? lookupAssumeInfo(appView, singleTarget) : null;
+ }
+ AssumeInfo singleTargetLookup =
+ singleTarget != null ? lookupAssumeInfo(appView, singleTarget) : null;
+ return singleTargetLookup != null
+ ? resolutionLookup.meet(singleTargetLookup)
+ : resolutionLookup;
+ }
+
+ public static AssumeInfo lookupAssumeInfo(
+ AppView<AppInfoWithLiveness> appView, DexClassAndMember<?, ?> member) {
+ DexMember<?, ?> reference = member.getReference();
+ ProguardMemberRule assumeNoSideEffectsRule = appView.appInfo().noSideEffects.get(reference);
+ ProguardMemberRule assumeValuesRule = appView.appInfo().assumedValues.get(reference);
+ if (assumeNoSideEffectsRule == null && assumeValuesRule == null) {
+ return null;
+ }
+ AssumeType type =
+ assumeNoSideEffectsRule != null
+ ? AssumeType.ASSUME_NO_SIDE_EFFECTS
+ : AssumeType.ASSUME_VALUES;
+ if ((assumeNoSideEffectsRule != null && assumeNoSideEffectsRule.hasReturnValue())
+ || assumeValuesRule == null) {
+ return new AssumeInfo(type, assumeNoSideEffectsRule);
+ }
+ return new AssumeInfo(type, assumeValuesRule);
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/peepholes/PeepholeLayout.java b/src/main/java/com/android/tools/r8/ir/optimize/peepholes/PeepholeLayout.java
index 6e36d48..86958a5 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/peepholes/PeepholeLayout.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/peepholes/PeepholeLayout.java
@@ -35,9 +35,9 @@
public Match test(InstructionListIterator it) {
if (backwards) {
- return testDirection(() -> it.hasPrevious(), () -> it.previous(), () -> it.next());
+ return testDirection(it::hasPrevious, it::previous, it::next);
} else {
- return testDirection(() -> it.hasNext(), () -> it.next(), () -> it.previous());
+ return testDirection(it::hasNext, it::next, it::previous);
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/templates/CfUtilityMethodsForCodeOptimizations.java b/src/main/java/com/android/tools/r8/ir/optimize/templates/CfUtilityMethodsForCodeOptimizations.java
index 9625b93..831a0b3 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/templates/CfUtilityMethodsForCodeOptimizations.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/templates/CfUtilityMethodsForCodeOptimizations.java
@@ -33,6 +33,8 @@
public static void registerSynthesizedCodeReferences(DexItemFactory factory) {
factory.createSynthesizedType("Ljava/lang/ClassCastException;");
+ factory.createSynthesizedType("Ljava/lang/IncompatibleClassChangeError;");
+ factory.createSynthesizedType("Ljava/lang/NoSuchMethodError;");
}
public static CfCode
@@ -73,6 +75,53 @@
ImmutableList.of());
}
+ public static CfCode
+ CfUtilityMethodsForCodeOptimizationsTemplates_throwIncompatibleClassChangeError(
+ InternalOptions options, DexMethod method) {
+ CfLabel label0 = new CfLabel();
+ return new CfCode(
+ method.holder,
+ 2,
+ 0,
+ ImmutableList.of(
+ label0,
+ new CfNew(options.itemFactory.createType("Ljava/lang/IncompatibleClassChangeError;")),
+ new CfStackInstruction(CfStackInstruction.Opcode.Dup),
+ new CfInvoke(
+ 183,
+ options.itemFactory.createMethod(
+ options.itemFactory.createType("Ljava/lang/IncompatibleClassChangeError;"),
+ options.itemFactory.createProto(options.itemFactory.voidType),
+ options.itemFactory.createString("<init>")),
+ false),
+ new CfThrow()),
+ ImmutableList.of(),
+ ImmutableList.of());
+ }
+
+ public static CfCode CfUtilityMethodsForCodeOptimizationsTemplates_throwNoSuchMethodError(
+ InternalOptions options, DexMethod method) {
+ CfLabel label0 = new CfLabel();
+ return new CfCode(
+ method.holder,
+ 2,
+ 0,
+ ImmutableList.of(
+ label0,
+ new CfNew(options.itemFactory.createType("Ljava/lang/NoSuchMethodError;")),
+ new CfStackInstruction(CfStackInstruction.Opcode.Dup),
+ new CfInvoke(
+ 183,
+ options.itemFactory.createMethod(
+ options.itemFactory.createType("Ljava/lang/NoSuchMethodError;"),
+ options.itemFactory.createProto(options.itemFactory.voidType),
+ options.itemFactory.createString("<init>")),
+ false),
+ new CfThrow()),
+ ImmutableList.of(),
+ ImmutableList.of());
+ }
+
public static CfCode CfUtilityMethodsForCodeOptimizationsTemplates_toStringIfNotNull(
InternalOptions options, DexMethod method) {
CfLabel label0 = new CfLabel();
diff --git a/src/main/java/com/android/tools/r8/ir/synthetic/FieldAccessorBuilder.java b/src/main/java/com/android/tools/r8/ir/synthetic/FieldAccessorBuilder.java
new file mode 100644
index 0000000..b02544d
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/synthetic/FieldAccessorBuilder.java
@@ -0,0 +1,134 @@
+// 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.synthetic;
+
+import com.android.tools.r8.cf.code.CfFieldInstruction;
+import com.android.tools.r8.cf.code.CfInstruction;
+import com.android.tools.r8.cf.code.CfLoad;
+import com.android.tools.r8.cf.code.CfReturn;
+import com.android.tools.r8.cf.code.CfReturnVoid;
+import com.android.tools.r8.cf.code.CfTryCatch;
+import com.android.tools.r8.graph.CfCode;
+import com.android.tools.r8.graph.DexClassAndField;
+import com.android.tools.r8.graph.DexField;
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.ir.code.ValueType;
+import com.android.tools.r8.utils.BooleanUtils;
+import com.android.tools.r8.utils.OptionalBool;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableList.Builder;
+import java.util.function.Consumer;
+import org.objectweb.asm.Opcodes;
+
+public class FieldAccessorBuilder {
+
+ private DexField field;
+ private OptionalBool isInstanceField = OptionalBool.unknown();
+ private OptionalBool isSetter = OptionalBool.unknown();
+ private DexMethod sourceMethod;
+
+ private FieldAccessorBuilder() {}
+
+ public static FieldAccessorBuilder builder() {
+ return new FieldAccessorBuilder();
+ }
+
+ public FieldAccessorBuilder apply(Consumer<FieldAccessorBuilder> consumer) {
+ consumer.accept(this);
+ return this;
+ }
+
+ public FieldAccessorBuilder setField(DexClassAndField field) {
+ return field.getAccessFlags().isStatic()
+ ? setStaticField(field.getReference())
+ : setInstanceField(field.getReference());
+ }
+
+ public FieldAccessorBuilder setGetter() {
+ isSetter = OptionalBool.FALSE;
+ return this;
+ }
+
+ public FieldAccessorBuilder setInstanceField(DexField field) {
+ this.field = field;
+ this.isInstanceField = OptionalBool.TRUE;
+ return this;
+ }
+
+ public FieldAccessorBuilder setSetter() {
+ isSetter = OptionalBool.TRUE;
+ return this;
+ }
+
+ public FieldAccessorBuilder setSourceMethod(DexMethod sourceMethod) {
+ this.sourceMethod = sourceMethod;
+ return this;
+ }
+
+ public FieldAccessorBuilder setStaticField(DexField field) {
+ this.field = field;
+ this.isInstanceField = OptionalBool.FALSE;
+ return this;
+ }
+
+ public CfCode build() {
+ assert validate();
+ int maxStack = 0;
+ int maxLocals = 0;
+ Builder<CfInstruction> instructions = ImmutableList.builder();
+ if (isInstanceField()) {
+ // Load the receiver.
+ instructions.add(new CfLoad(ValueType.OBJECT, maxLocals));
+ maxStack += 1;
+ maxLocals += 1;
+ }
+
+ if (isSetter()) {
+ // Load the argument.
+ ValueType fieldType = ValueType.fromDexType(field.getType());
+ instructions.add(new CfLoad(fieldType, maxLocals));
+ maxLocals += fieldType.requiredRegisters();
+ }
+
+ // Get or set the field.
+ int opcode =
+ Opcodes.GETSTATIC + BooleanUtils.intValue(isSetter()) + (isInstanceField.ordinal() << 1);
+ instructions.add(new CfFieldInstruction(opcode, field, field));
+
+ // Return.
+ if (isSetter()) {
+ instructions.add(new CfReturnVoid());
+ } else {
+ ValueType fieldType = ValueType.fromDexType(field.getType());
+ instructions.add(new CfReturn(fieldType));
+ }
+
+ ImmutableList<CfTryCatch> tryCatchRanges = ImmutableList.of();
+ ImmutableList<CfCode.LocalVariableInfo> localVariables = ImmutableList.of();
+ return new CfCode(
+ sourceMethod.getHolderType(),
+ maxStack,
+ maxLocals,
+ instructions.build(),
+ tryCatchRanges,
+ localVariables);
+ }
+
+ private boolean isSetter() {
+ return isSetter.isTrue();
+ }
+
+ private boolean isInstanceField() {
+ return isInstanceField.isTrue();
+ }
+
+ private boolean validate() {
+ assert field != null;
+ assert !isInstanceField.isUnknown();
+ assert !isSetter.isUnknown();
+ assert sourceMethod != null;
+ return true;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/synthetic/FieldAccessorSourceCode.java b/src/main/java/com/android/tools/r8/ir/synthetic/FieldAccessorSourceCode.java
deleted file mode 100644
index 8936c73..0000000
--- a/src/main/java/com/android/tools/r8/ir/synthetic/FieldAccessorSourceCode.java
+++ /dev/null
@@ -1,55 +0,0 @@
-// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
-// for details. All rights reserved. Use of this source code is governed by a
-// BSD-style license that can be found in the LICENSE file.
-
-package com.android.tools.r8.ir.synthetic;
-
-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.code.ValueType;
-import com.android.tools.r8.ir.conversion.IRBuilder;
-import com.android.tools.r8.ir.desugar.NestBasedAccessDesugaring.DexFieldWithAccess;
-
-// Source code representing simple forwarding method.
-public final class FieldAccessorSourceCode extends SyntheticSourceCode {
-
- private final DexFieldWithAccess fieldWithAccess;
-
- public FieldAccessorSourceCode(
- DexType receiver,
- DexMethod method,
- Position callerPosition,
- DexMethod originalMethod,
- DexFieldWithAccess field) {
- super(receiver, method, callerPosition, originalMethod);
- this.fieldWithAccess = field;
- assert method.proto.returnType == fieldWithAccess.getType() || fieldWithAccess.isPut();
- }
-
- @Override
- protected void prepareInstructions() {
- if (fieldWithAccess.isInstanceGet()) {
- ValueType valueType = ValueType.fromDexType(proto.returnType);
- int objReg = getParamRegister(0);
- int returnReg = nextRegister(valueType);
- add(builder -> builder.addInstanceGet(returnReg, objReg, fieldWithAccess.getField()));
- add(builder -> builder.addReturn(returnReg));
- } else if (fieldWithAccess.isStaticGet()) {
- ValueType valueType = ValueType.fromDexType(proto.returnType);
- int returnReg = nextRegister(valueType);
- add(builder -> builder.addStaticGet(returnReg, fieldWithAccess.getField()));
- add(builder -> builder.addReturn(returnReg));
- } else if (fieldWithAccess.isInstancePut()) {
- int objReg = getParamRegister(0);
- int putValueReg = getParamRegister(1);
- add(builder -> builder.addInstancePut(putValueReg, objReg, fieldWithAccess.getField()));
- add(IRBuilder::addReturn);
- } else {
- assert fieldWithAccess.isStaticPut();
- int putValueReg = getParamRegister(0);
- add(builder -> builder.addStaticPut(putValueReg, fieldWithAccess.getField()));
- add(IRBuilder::addReturn);
- }
- }
-}
diff --git a/src/main/java/com/android/tools/r8/ir/synthetic/ForwardMethodBuilder.java b/src/main/java/com/android/tools/r8/ir/synthetic/ForwardMethodBuilder.java
index 8d8ca9f..794fd3e 100644
--- a/src/main/java/com/android/tools/r8/ir/synthetic/ForwardMethodBuilder.java
+++ b/src/main/java/com/android/tools/r8/ir/synthetic/ForwardMethodBuilder.java
@@ -20,8 +20,10 @@
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.ir.code.ValueType;
+import com.android.tools.r8.utils.BooleanUtils;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableList.Builder;
+import java.util.function.Consumer;
import org.objectweb.asm.Opcodes;
public class ForwardMethodBuilder {
@@ -41,6 +43,7 @@
private DexMethod sourceMethod = null;
private DexMethod targetMethod = null;
+ private boolean sourceMethodHasExtraUnusedParameter = false;
private boolean staticSource = false;
private InvokeType invokeType = null;
@@ -53,12 +56,24 @@
this.factory = factory;
}
+ public ForwardMethodBuilder apply(Consumer<ForwardMethodBuilder> fn) {
+ fn.accept(this);
+ return this;
+ }
+
public ForwardMethodBuilder setNonStaticSource(DexMethod method) {
sourceMethod = method;
staticSource = false;
return this;
}
+ public ForwardMethodBuilder setNonStaticSourceWithExtraUnusedParameter(DexMethod method) {
+ sourceMethod = method;
+ staticSource = false;
+ sourceMethodHasExtraUnusedParameter = true;
+ return this;
+ }
+
public ForwardMethodBuilder setStaticSource(DexMethod method) {
sourceMethod = method;
staticSource = true;
@@ -79,6 +94,10 @@
return this;
}
+ public ForwardMethodBuilder setConstructorTarget(DexMethod method) {
+ return setDirectTarget(method, false);
+ }
+
public ForwardMethodBuilder setDirectTarget(DexMethod method, boolean isInterface) {
targetMethod = method;
invokeType = InvokeType.SPECIAL;
@@ -126,7 +145,9 @@
maxLocals += 1;
}
DexType[] sourceParameters = getSourceParameters();
- for (int i = 0; i < sourceParameters.length; i++) {
+ for (int i = 0;
+ i < sourceParameters.length - BooleanUtils.intValue(sourceMethodHasExtraUnusedParameter);
+ i++) {
DexType parameter = sourceParameters[i];
ValueType parameterType = ValueType.fromDexType(parameter);
instructions.add(new CfLoad(parameterType, maxLocals));
@@ -212,7 +233,9 @@
}
private int sourceArguments() {
- return sourceMethod.getParameters().size() + (isStaticSource() ? 0 : 1);
+ return sourceMethod.getParameters().size()
+ + (isStaticSource() ? 0 : 1)
+ - BooleanUtils.intValue(sourceMethodHasExtraUnusedParameter);
}
private int targetArguments() {
diff --git a/src/main/java/com/android/tools/r8/jar/CfApplicationWriter.java b/src/main/java/com/android/tools/r8/jar/CfApplicationWriter.java
index ef724ec..cf6a117 100644
--- a/src/main/java/com/android/tools/r8/jar/CfApplicationWriter.java
+++ b/src/main/java/com/android/tools/r8/jar/CfApplicationWriter.java
@@ -40,7 +40,7 @@
import com.android.tools.r8.naming.NamingLens;
import com.android.tools.r8.naming.ProguardMapSupplier;
import com.android.tools.r8.references.Reference;
-import com.android.tools.r8.synthesis.SyntheticItems;
+import com.android.tools.r8.synthesis.SyntheticNaming;
import com.android.tools.r8.utils.AsmUtils;
import com.android.tools.r8.utils.ExceptionUtils;
import com.android.tools.r8.utils.InternalOptions;
@@ -124,7 +124,7 @@
marker.isRelocator() ? Optional.empty() : Optional.of(marker.toString());
LensCodeRewriterUtils rewriter = new LensCodeRewriterUtils(appView);
for (DexProgramClass clazz : application.classes()) {
- assert SyntheticItems.verifyNotInternalSynthetic(clazz.getType());
+ assert SyntheticNaming.verifyNotInternalSynthetic(clazz.getType());
try {
writeClass(clazz, consumer, rewriter, markerString);
} catch (ClassTooLargeException e) {
@@ -194,6 +194,7 @@
for (int i = 0; i < clazz.interfaces.values.length; i++) {
interfaces[i] = namingLens.lookupInternalName(clazz.interfaces.values[i]);
}
+ assert SyntheticNaming.verifyNotInternalSynthetic(name);
writer.visit(version.raw(), access, name, signature, superName, interfaces);
writeAnnotations(writer::visitAnnotation, clazz.annotations().annotations);
ImmutableMap<DexString, DexValue> defaults = getAnnotationDefaults(clazz.annotations());
diff --git a/src/main/java/com/android/tools/r8/naming/ClassNameMinifier.java b/src/main/java/com/android/tools/r8/naming/ClassNameMinifier.java
index e7ca5b5..771c336 100644
--- a/src/main/java/com/android/tools/r8/naming/ClassNameMinifier.java
+++ b/src/main/java/com/android/tools/r8/naming/ClassNameMinifier.java
@@ -100,13 +100,6 @@
}
ClassRenaming computeRenaming(Timing timing) {
- return computeRenaming(timing, Collections.emptyMap());
- }
-
- ClassRenaming computeRenaming(Timing timing, Map<DexType, DexString> syntheticClasses) {
- // Externally defined synthetic classes populate an initial renaming.
- renaming.putAll(syntheticClasses);
-
// Collect names we have to keep.
timing.begin("reserve");
for (DexClass clazz : classes) {
diff --git a/src/main/java/com/android/tools/r8/naming/InterfaceMethodNameMinifier.java b/src/main/java/com/android/tools/r8/naming/InterfaceMethodNameMinifier.java
index a8bd96c..0f5924d 100644
--- a/src/main/java/com/android/tools/r8/naming/InterfaceMethodNameMinifier.java
+++ b/src/main/java/com/android/tools/r8/naming/InterfaceMethodNameMinifier.java
@@ -23,7 +23,6 @@
import com.google.common.collect.Sets;
import java.io.PrintStream;
import java.util.ArrayList;
-import java.util.Collection;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
@@ -397,7 +396,7 @@
return Comparator.comparing(globalStateMap::get);
}
- private void reserveNamesInInterfaces(Collection<DexClass> interfaces) {
+ private void reserveNamesInInterfaces(Iterable<DexClass> interfaces) {
for (DexClass iface : interfaces) {
assert iface.isInterface();
minifierState.allocateReservationStateAndReserve(iface.type, iface.type);
@@ -407,7 +406,7 @@
}
}
- void assignNamesToInterfaceMethods(Timing timing, Collection<DexClass> interfaces) {
+ void assignNamesToInterfaceMethods(Timing timing, Iterable<DexClass> interfaces) {
timing.begin("Interface minification");
// Reserve all the names that are required for interfaces.
timing.begin("Reserve direct and compute hierarchy");
@@ -643,19 +642,26 @@
clazz -> {
// TODO(b/133091438): Extend the if check to test for !clazz.isLibrary().
if (!clazz.isInterface()) {
- for (DexType directlyImplemented :
- appView.appInfo().implementedInterfaces(clazz.type)) {
- InterfaceReservationState iState = interfaceStateMap.get(directlyImplemented);
- if (iState != null) {
- DexType frontierType = minifierState.getFrontier(clazz.type);
- iState.addReservationType(frontierType);
- // The reservation state should already be added, but if a class is extending
- // an interface, we will not visit the class during the sub-type traversel
- if (minifierState.getReservationState(clazz.type) == null) {
- minifierState.allocateReservationStateAndReserve(clazz.type, frontierType);
- }
- }
- }
+ appView
+ .appInfo()
+ .implementedInterfaces(clazz.type)
+ .forEach(
+ (directlyImplemented, ignoreIsKnownAndReserveInAllCases) -> {
+ InterfaceReservationState iState =
+ interfaceStateMap.get(directlyImplemented);
+ if (iState != null) {
+ DexType frontierType = minifierState.getFrontier(clazz.type);
+ iState.addReservationType(frontierType);
+ // The reservation state should already be added, but if a class is
+ // extending
+ // an interface, we will not visit the class during the sub-type
+ // traversel
+ if (minifierState.getReservationState(clazz.type) == null) {
+ minifierState.allocateReservationStateAndReserve(
+ clazz.type, frontierType);
+ }
+ }
+ });
}
});
}
diff --git a/src/main/java/com/android/tools/r8/naming/MethodNameMinifier.java b/src/main/java/com/android/tools/r8/naming/MethodNameMinifier.java
index 49dcd83..0f50ecb 100644
--- a/src/main/java/com/android/tools/r8/naming/MethodNameMinifier.java
+++ b/src/main/java/com/android/tools/r8/naming/MethodNameMinifier.java
@@ -16,7 +16,6 @@
import com.google.common.collect.BiMap;
import com.google.common.collect.HashBiMap;
import com.google.common.collect.ImmutableMap;
-import java.util.Collection;
import java.util.HashMap;
import java.util.IdentityHashMap;
import java.util.Map;
@@ -174,7 +173,7 @@
}
}
- MethodRenaming computeRenaming(Collection<DexClass> interfaces, Timing timing) {
+ MethodRenaming computeRenaming(Iterable<DexClass> interfaces, Timing timing) {
// Phase 1: Reserve all the names that need to be kept and allocate linked state in the
// library part.
timing.begin("Phase 1");
diff --git a/src/main/java/com/android/tools/r8/naming/Minifier.java b/src/main/java/com/android/tools/r8/naming/Minifier.java
index 4bc638a..c00993f 100644
--- a/src/main/java/com/android/tools/r8/naming/Minifier.java
+++ b/src/main/java/com/android/tools/r8/naming/Minifier.java
@@ -3,6 +3,7 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.naming;
+import static com.android.tools.r8.graph.DexApplication.classesWithDeterministicOrder;
import static com.android.tools.r8.utils.StringUtils.EMPTY_CHAR_ARRAY;
import static com.android.tools.r8.utils.SymbolGenerationUtils.PRIMITIVE_TYPE_NAMES;
@@ -26,11 +27,10 @@
import com.android.tools.r8.utils.SymbolGenerationUtils;
import com.android.tools.r8.utils.SymbolGenerationUtils.MixedCasing;
import com.android.tools.r8.utils.Timing;
-import java.util.Comparator;
+import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
-import java.util.TreeSet;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.function.BiPredicate;
@@ -48,8 +48,7 @@
assert appView.options().isMinifying();
SubtypingInfo subtypingInfo = appView.appInfo().computeSubtypingInfo();
timing.begin("ComputeInterfaces");
- Set<DexClass> interfaces = new TreeSet<>(Comparator.comparing(a -> a.type));
- interfaces.addAll(appView.appInfo().computeReachableInterfaces());
+ List<DexClass> interfaces = computeReachableInterfacesWithDeterministicOrder();
timing.end();
timing.begin("MinifyClasses");
ClassNameMinifier classNameMinifier =
@@ -92,6 +91,12 @@
return lens;
}
+ private List<DexClass> computeReachableInterfacesWithDeterministicOrder() {
+ List<DexClass> interfaces = new ArrayList<>();
+ appView.appInfo().forEachReachableInterface(interfaces::add);
+ return classesWithDeterministicOrder(interfaces);
+ }
+
abstract static class BaseMinificationNamingStrategy {
// We have to ensure that the names proposed by the minifier is not used in the obfuscation
diff --git a/src/main/java/com/android/tools/r8/naming/ProguardMapMinifier.java b/src/main/java/com/android/tools/r8/naming/ProguardMapMinifier.java
index 6d8c889..b75956e 100644
--- a/src/main/java/com/android/tools/r8/naming/ProguardMapMinifier.java
+++ b/src/main/java/com/android/tools/r8/naming/ProguardMapMinifier.java
@@ -4,6 +4,8 @@
package com.android.tools.r8.naming;
+import static com.android.tools.r8.graph.DexApplication.classesWithDeterministicOrder;
+
import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexClass;
@@ -35,7 +37,9 @@
import com.google.common.collect.BiMap;
import com.google.common.collect.HashBiMap;
import com.google.common.collect.Maps;
+import com.google.common.collect.Sets;
import java.util.ArrayDeque;
+import java.util.Comparator;
import java.util.Deque;
import java.util.HashMap;
import java.util.HashSet;
@@ -73,9 +77,8 @@
private final SeedMapper seedMapper;
private final BiMap<DexType, DexString> mappedNames = HashBiMap.create();
// To keep the order deterministic, we sort the classes by their type, which is a unique key.
- private final Set<DexClass> mappedClasses = new TreeSet<>((a, b) -> a.type.compareTo(b.type));
+ private final Set<DexClass> mappedClasses = Sets.newIdentityHashSet();
private final Map<DexReference, MemberNaming> memberNames = Maps.newIdentityHashMap();
- private final Map<DexType, DexString> syntheticCompanionClasses = Maps.newIdentityHashMap();
private final Map<DexMethod, DexString> defaultInterfaceMethodImplementationNames =
Maps.newIdentityHashMap();
private final Map<DexMethod, DexString> additionalMethodNamings = Maps.newIdentityHashMap();
@@ -95,15 +98,15 @@
Set<DexReference> notMappedReferences = new HashSet<>();
timing.begin("MappingInterfaces");
- Set<DexClass> interfaces = new TreeSet<>((a, b) -> a.type.compareTo(b.type));
+ Set<DexClass> interfaces = new TreeSet<>(Comparator.comparing(DexClass::getType));
Consumer<DexClass> consumer =
- dexClass -> {
- if (dexClass.isInterface()) {
+ clazz -> {
+ if (clazz.isInterface()) {
// Only visit top level interfaces because computeMapping will visit the hierarchy.
- if (dexClass.interfaces.isEmpty()) {
- computeMapping(dexClass.type, nonPrivateMembers, notMappedReferences, subtypingInfo);
+ if (clazz.interfaces.isEmpty()) {
+ computeMapping(clazz.type, nonPrivateMembers, notMappedReferences, subtypingInfo);
}
- interfaces.add(dexClass);
+ interfaces.add(clazz);
}
};
// For union-find of interface methods we also need to add the library types above live types.
@@ -141,9 +144,8 @@
// in the ClassNameMinifier that the strategy should produce a "fresh" name so we just
// use the existing strategy.
new MinificationPackageNamingStrategy(appView),
- mappedClasses);
- ClassRenaming classRenaming =
- classNameMinifier.computeRenaming(timing, syntheticCompanionClasses);
+ classesWithDeterministicOrder(mappedClasses));
+ ClassRenaming classRenaming = classNameMinifier.computeRenaming(timing);
timing.end();
ApplyMappingMemberNamingStrategy nameStrategy =
@@ -341,11 +343,10 @@
}
// TODO(b/150736225): Is this sound? What if the type is a library type that has been pruned?
DexClass dexClass = appView.appInfo().definitionForWithoutExistenceAssert(type);
- if (dexClass == null) {
+ if (dexClass == null || dexClass.isClasspathClass()) {
computeDefaultInterfaceMethodMappingsForType(
type,
classNaming,
- syntheticCompanionClasses,
defaultInterfaceMethodImplementationNames);
}
}
@@ -354,7 +355,6 @@
private void computeDefaultInterfaceMethodMappingsForType(
DexType type,
ClassNamingForMapApplier classNaming,
- Map<DexType, DexString> syntheticCompanionClasses,
Map<DexMethod, DexString> defaultInterfaceMethodImplementationNames) {
// If the class does not resolve, then check if it is a companion class for an interface on
// the class path.
@@ -366,7 +366,6 @@
if (interfaceType == null || !interfaceType.isClasspathClass()) {
return;
}
- syntheticCompanionClasses.put(type, factory.createString(classNaming.renamedName));
for (List<MemberNaming> namings : classNaming.getQualifiedMethodMembers().values()) {
// If the qualified name has been mapped to multiple names we can't compute a mapping (and it
// should not be possible that this is a default interface method in that case.)
diff --git a/src/main/java/com/android/tools/r8/optimize/ClassAndMemberPublicizer.java b/src/main/java/com/android/tools/r8/optimize/ClassAndMemberPublicizer.java
index 30d9b7d..a675776 100644
--- a/src/main/java/com/android/tools/r8/optimize/ClassAndMemberPublicizer.java
+++ b/src/main/java/com/android/tools/r8/optimize/ClassAndMemberPublicizer.java
@@ -10,7 +10,6 @@
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexApplication;
-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.DexMethod;
@@ -83,9 +82,7 @@
// Phase 2: Visit classes and promote class/member to public if possible.
timing.begin("Phase 2: promoteToPublic");
- for (DexClass iface : appView.appInfo().computeReachableInterfaces()) {
- publicizeType(iface.type);
- }
+ appView.appInfo().forEachReachableInterface(clazz -> publicizeType(clazz.getType()));
publicizeType(appView.dexItemFactory().objectType);
timing.end();
diff --git a/src/main/java/com/android/tools/r8/repackaging/Repackaging.java b/src/main/java/com/android/tools/r8/repackaging/Repackaging.java
index 51e419b..eba0a87 100644
--- a/src/main/java/com/android/tools/r8/repackaging/Repackaging.java
+++ b/src/main/java/com/android/tools/r8/repackaging/Repackaging.java
@@ -8,7 +8,6 @@
import static com.android.tools.r8.utils.DescriptorUtils.DESCRIPTOR_PACKAGE_SEPARATOR;
import static com.android.tools.r8.utils.DescriptorUtils.INNER_CLASS_SEPARATOR;
-import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexField;
import com.android.tools.r8.graph.DexItemFactory;
@@ -73,8 +72,7 @@
return lens;
}
- public static boolean verifyIdentityRepackaging(
- AppView<? extends AppInfoWithClassHierarchy> appView) {
+ public static boolean verifyIdentityRepackaging(AppView<AppInfoWithLiveness> appView) {
// Running the tree fixer with an identity mapping helps ensure that the fixup of of items is
// complete as the rewrite replaces all items regardless of repackaging.
// The identity mapping should result in no move callbacks being called.
@@ -140,10 +138,11 @@
return null;
}
RepackagingLens.Builder lensBuilder = new RepackagingLens.Builder();
+ RepackagingTreeFixer repackagingTreeFixer =
+ new RepackagingTreeFixer(appView, mappings, lensBuilder);
List<DexProgramClass> newProgramClasses =
new ArrayList<>(
- new RepackagingTreeFixer(appView, mappings, lensBuilder)
- .fixupClasses(appView.appInfo().classesWithDeterministicOrder()));
+ repackagingTreeFixer.fixupClasses(appView.appInfo().classesWithDeterministicOrder()));
appBuilder.replaceProgramClasses(newProgramClasses);
RepackagingLens lens = lensBuilder.build(appView);
new AnnotationFixer(lens).run(appBuilder.getProgramClasses());
@@ -156,10 +155,14 @@
private final Builder lensBuilder;
public RepackagingTreeFixer(
- AppView<?> appView, BiMap<DexType, DexType> mappings, Builder lensBuilder) {
+ AppView<AppInfoWithLiveness> appView,
+ BiMap<DexType, DexType> mappings,
+ Builder lensBuilder) {
super(appView);
+ assert mappings != null;
this.mappings = mappings;
this.lensBuilder = lensBuilder;
+ recordFailedResolutionChanges();
}
@Override
diff --git a/src/main/java/com/android/tools/r8/retrace/internal/StackTraceElementStringProxy.java b/src/main/java/com/android/tools/r8/retrace/internal/StackTraceElementStringProxy.java
index f0ba88d..6decd5e 100644
--- a/src/main/java/com/android/tools/r8/retrace/internal/StackTraceElementStringProxy.java
+++ b/src/main/java/com/android/tools/r8/retrace/internal/StackTraceElementStringProxy.java
@@ -15,7 +15,6 @@
import com.android.tools.r8.retrace.RetracedType;
import com.android.tools.r8.retrace.StackTraceElementProxy;
import com.android.tools.r8.utils.StringUtils;
-import com.android.tools.r8.utils.StringUtils.BraceType;
import com.android.tools.r8.utils.TriFunction;
import java.util.ArrayList;
import java.util.List;
@@ -287,7 +286,7 @@
return original.getMethodArguments();
}
return StringUtils.join(
- retraced.getMethodArguments(), ",", BraceType.NONE, RetracedType::getTypeName);
+ ",", retraced.getMethodArguments(), RetracedType::getTypeName);
});
orderedIndices.add(methodArguments);
return this;
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 6b316f7..363cb30 100644
--- a/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java
+++ b/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java
@@ -45,9 +45,9 @@
import com.android.tools.r8.ir.analysis.type.ClassTypeElement;
import com.android.tools.r8.ir.code.Invoke.Type;
import com.android.tools.r8.ir.desugar.DesugaredLibraryAPIConverter;
+import com.android.tools.r8.ir.desugar.DesugaredLibraryRetargeter;
import com.android.tools.r8.ir.desugar.InterfaceMethodRewriter;
import com.android.tools.r8.ir.desugar.LambdaDescriptor;
-import com.android.tools.r8.ir.desugar.TwrCloseResourceRewriter;
import com.android.tools.r8.synthesis.CommittedItems;
import com.android.tools.r8.utils.CollectionUtils;
import com.android.tools.r8.utils.InternalOptions;
@@ -89,8 +89,11 @@
*/
private final Set<DexMethod> targetedMethods;
- /** Set of targets that lead to resolution errors, such as non-existing or invalid targets. */
- private final Set<DexMethod> failedResolutionTargets;
+ /** Method targets that lead to resolution errors such as non-existing or invalid targets. */
+ private final Set<DexMethod> failedMethodResolutionTargets;
+
+ /** Field targets that lead to resolution errors, such as non-existing or invalid targets. */
+ private final Set<DexField> failedFieldResolutionTargets;
/**
* Set of program methods that are used as the bootstrap method for an invoke-dynamic instruction.
@@ -197,7 +200,8 @@
MissingClasses missingClasses,
Set<DexType> liveTypes,
Set<DexMethod> targetedMethods,
- Set<DexMethod> failedResolutionTargets,
+ Set<DexMethod> failedMethodResolutionTargets,
+ Set<DexField> failedFieldResolutionTargets,
Set<DexMethod> bootstrapMethods,
Set<DexMethod> methodsTargetedByInvokeDynamic,
Set<DexMethod> virtualMethodsTargetedByInvokeDirect,
@@ -235,7 +239,8 @@
this.deadProtoTypes = deadProtoTypes;
this.liveTypes = liveTypes;
this.targetedMethods = targetedMethods;
- this.failedResolutionTargets = failedResolutionTargets;
+ this.failedMethodResolutionTargets = failedMethodResolutionTargets;
+ this.failedFieldResolutionTargets = failedFieldResolutionTargets;
this.bootstrapMethods = bootstrapMethods;
this.methodsTargetedByInvokeDynamic = methodsTargetedByInvokeDynamic;
this.virtualMethodsTargetedByInvokeDirect = virtualMethodsTargetedByInvokeDirect;
@@ -269,7 +274,7 @@
this.switchMaps = switchMaps;
this.lockCandidates = lockCandidates;
this.initClassReferences = initClassReferences;
- verify();
+ assert verify();
}
private AppInfoWithLiveness(AppInfoWithLiveness previous, CommittedItems committedItems) {
@@ -279,9 +284,10 @@
previous.getMainDexClasses(),
previous.deadProtoTypes,
previous.getMissingClasses(),
- CollectionUtils.mergeSets(previous.liveTypes, committedItems.getCommittedTypes()),
+ CollectionUtils.mergeSets(previous.liveTypes, committedItems.getCommittedProgramTypes()),
previous.targetedMethods,
- previous.failedResolutionTargets,
+ previous.failedMethodResolutionTargets,
+ previous.failedFieldResolutionTargets,
previous.bootstrapMethods,
previous.methodsTargetedByInvokeDynamic,
previous.virtualMethodsTargetedByInvokeDirect,
@@ -328,7 +334,8 @@
? Sets.difference(previous.liveTypes, prunedItems.getRemovedClasses())
: previous.liveTypes,
previous.targetedMethods,
- previous.failedResolutionTargets,
+ previous.failedMethodResolutionTargets,
+ previous.failedFieldResolutionTargets,
previous.bootstrapMethods,
previous.methodsTargetedByInvokeDynamic,
previous.virtualMethodsTargetedByInvokeDirect,
@@ -366,10 +373,11 @@
previous.initClassReferences);
}
- private void verify() {
+ private boolean verify() {
assert keepInfo.verifyPinnedTypesAreLive(liveTypes);
assert objectAllocationInfoCollection.verifyAllocatedTypesAreLive(
liveTypes, getMissingClasses(), this);
+ return true;
}
private static KeepInfoCollection extendPinnedItems(
@@ -419,7 +427,8 @@
this.deadProtoTypes = previous.deadProtoTypes;
this.liveTypes = previous.liveTypes;
this.targetedMethods = previous.targetedMethods;
- this.failedResolutionTargets = previous.failedResolutionTargets;
+ this.failedMethodResolutionTargets = previous.failedMethodResolutionTargets;
+ this.failedFieldResolutionTargets = previous.failedFieldResolutionTargets;
this.bootstrapMethods = previous.bootstrapMethods;
this.methodsTargetedByInvokeDynamic = previous.methodsTargetedByInvokeDynamic;
this.virtualMethodsTargetedByInvokeDirect = previous.virtualMethodsTargetedByInvokeDirect;
@@ -454,7 +463,7 @@
this.lockCandidates = previous.lockCandidates;
this.initClassReferences = previous.initClassReferences;
previous.markObsolete();
- verify();
+ assert verify();
}
public static AppInfoWithLivenessModifier modifier() {
@@ -470,8 +479,7 @@
// TODO(b/150693139): Remove these exceptions once fixed.
|| InterfaceMethodRewriter.isCompanionClassType(type)
|| InterfaceMethodRewriter.isEmulatedLibraryClassType(type)
- || type.toDescriptorString().startsWith("Lj$/$r8$retargetLibraryMember$")
- || TwrCloseResourceRewriter.isUtilityClassDescriptor(type)
+ || DesugaredLibraryRetargeter.isRetargetType(type, options())
// TODO(b/150736225): Not sure how to remove these.
|| DesugaredLibraryAPIConverter.isVivifiedType(type)
: "Failed lookup of non-missing type: " + type;
@@ -533,11 +541,15 @@
}
public boolean isFailedResolutionTarget(DexMethod method) {
- return failedResolutionTargets.contains(method);
+ return failedMethodResolutionTargets.contains(method);
}
- public Set<DexMethod> getFailedResolutionTargets() {
- return failedResolutionTargets;
+ public Set<DexMethod> getFailedMethodResolutionTargets() {
+ return failedMethodResolutionTargets;
+ }
+
+ public Set<DexField> getFailedFieldResolutionTargets() {
+ return failedFieldResolutionTargets;
}
public boolean isBootstrapMethod(DexMethod method) {
@@ -604,8 +616,7 @@
return reprocess;
}
- public Collection<DexClass> computeReachableInterfaces() {
- Set<DexClass> interfaces = Sets.newIdentityHashSet();
+ public void forEachReachableInterface(Consumer<DexClass> consumer) {
WorkList<DexType> worklist = WorkList.newIdentityWorkList();
worklist.addIfNotSeen(objectAllocationInfoCollection.getInstantiatedLambdaInterfaces());
for (DexProgramClass clazz : classes()) {
@@ -618,14 +629,13 @@
continue;
}
if (definition.isInterface()) {
- interfaces.add(definition);
+ consumer.accept(definition);
}
if (definition.superType != null) {
worklist.addIfNotSeen(definition.superType);
}
worklist.addIfNotSeen(definition.interfaces.values);
}
- return interfaces;
}
/**
@@ -999,7 +1009,8 @@
getMissingClasses().commitSyntheticItems(committedItems),
lens.rewriteTypes(liveTypes),
lens.rewriteMethods(targetedMethods),
- lens.rewriteMethods(failedResolutionTargets),
+ lens.rewriteMethods(failedMethodResolutionTargets),
+ lens.rewriteFields(failedFieldResolutionTargets),
lens.rewriteMethods(bootstrapMethods),
lens.rewriteMethods(methodsTargetedByInvokeDynamic),
lens.rewriteMethods(virtualMethodsTargetedByInvokeDirect),
@@ -1351,12 +1362,7 @@
// The type java.lang.Object could be any instantiated type. Assume a finalizer exists.
return true;
}
- for (DexType iface : type.getInterfaces()) {
- if (mayHaveFinalizer(iface)) {
- return true;
- }
- }
- return false;
+ return type.getInterfaces().anyMatch((iface, isKnown) -> mayHaveFinalizer(iface));
}
return mayHaveFinalizer(type.getClassType());
}
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 c15fdb8..f7ac800 100644
--- a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
+++ b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
@@ -5,6 +5,8 @@
import static com.android.tools.r8.graph.DexProgramClass.asProgramClassOrNull;
import static com.android.tools.r8.graph.FieldAccessInfoImpl.MISSING_FIELD_ACCESS_INFO;
+import static com.android.tools.r8.ir.desugar.LambdaDescriptor.isLambdaMetafactoryMethod;
+import static com.android.tools.r8.ir.optimize.enums.UnboxedEnumMemberRelocator.ENUM_UNBOXING_UTILITY_CLASS_SUFFIX;
import static com.android.tools.r8.naming.IdentifierNameStringUtils.identifyIdentifier;
import static com.android.tools.r8.naming.IdentifierNameStringUtils.isReflectionMethod;
import static com.android.tools.r8.shaking.AnnotationRemover.shouldKeepAnnotation;
@@ -19,6 +21,7 @@
import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.CfCode;
+import com.android.tools.r8.graph.Code;
import com.android.tools.r8.graph.DexAnnotation;
import com.android.tools.r8.graph.DexAnnotationSet;
import com.android.tools.r8.graph.DexApplication;
@@ -55,6 +58,7 @@
import com.android.tools.r8.graph.LookupLambdaTarget;
import com.android.tools.r8.graph.LookupTarget;
import com.android.tools.r8.graph.MethodAccessInfoCollection;
+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.ProgramField;
@@ -86,6 +90,8 @@
import com.android.tools.r8.ir.desugar.LambdaClass;
import com.android.tools.r8.ir.desugar.LambdaDescriptor;
import com.android.tools.r8.ir.desugar.LambdaRewriter;
+import com.android.tools.r8.ir.desugar.TwrCloseResourceRewriter;
+import com.android.tools.r8.ir.desugar.nest.NestBasedAccessDesugaring;
import com.android.tools.r8.kotlin.KotlinMetadataEnqueuerExtension;
import com.android.tools.r8.logging.Log;
import com.android.tools.r8.naming.identifiernamestring.IdentifierNameStringLookupResult;
@@ -103,12 +109,15 @@
import com.android.tools.r8.utils.Action;
import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.InternalOptions.DesugarState;
+import com.android.tools.r8.utils.InternalOptions.OutlineOptions;
import com.android.tools.r8.utils.IteratorUtils;
+import com.android.tools.r8.utils.ListUtils;
import com.android.tools.r8.utils.MethodSignatureEquivalence;
import com.android.tools.r8.utils.OptionalBool;
import com.android.tools.r8.utils.Pair;
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.Timing;
import com.android.tools.r8.utils.Visibility;
import com.android.tools.r8.utils.WorkList;
@@ -118,6 +127,7 @@
import com.android.tools.r8.utils.collections.SortedProgramMethodSet;
import com.google.common.base.Equivalence.Wrapper;
import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Iterables;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import com.google.common.collect.Sets.SetView;
@@ -134,6 +144,7 @@
import java.util.IdentityHashMap;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
+import java.util.LinkedList;
import java.util.List;
import java.util.ListIterator;
import java.util.Map;
@@ -162,7 +173,8 @@
public enum Mode {
INITIAL_TREE_SHAKING,
FINAL_TREE_SHAKING,
- MAIN_DEX_TRACING,
+ INITIAL_MAIN_DEX_TRACING,
+ FINAL_MAIN_DEX_TRACING,
WHY_ARE_YOU_KEEPING;
public boolean isInitialTreeShaking() {
@@ -177,8 +189,16 @@
return isInitialTreeShaking() || isFinalTreeShaking();
}
- public boolean isTracingMainDex() {
- return this == MAIN_DEX_TRACING;
+ public boolean isInitialMainDexTracing() {
+ return this == INITIAL_MAIN_DEX_TRACING;
+ }
+
+ public boolean isFinalMainDexTracing() {
+ return this == FINAL_MAIN_DEX_TRACING;
+ }
+
+ public boolean isMainDexTracing() {
+ return isInitialMainDexTracing() || isFinalMainDexTracing();
}
public boolean isWhyAreYouKeeping() {
@@ -198,10 +218,10 @@
// Don't hold a direct pointer to app info (use appView).
private AppInfoWithClassHierarchy appInfo;
private final AppView<AppInfoWithClassHierarchy> appView;
+ private final ExecutorService executorService;
private SubtypingInfo subtypingInfo;
private final InternalOptions options;
private RootSet rootSet;
- private ProguardClassFilter dontWarnPatterns;
private final EnqueuerUseRegistryFactory useRegistryFactory;
private AnnotationRemover.Builder annotationRemoverBuilder;
private final EnqueuerDefinitionSupplier enqueuerDefinitionSupplier =
@@ -283,7 +303,10 @@
private final SetWithReason<DexEncodedMethod> targetedMethods;
/** Set of methods that have invalid resolutions or lookups. */
- private final Set<DexMethod> failedResolutionTargets;
+ private final Set<DexMethod> failedMethodResolutionTargets;
+
+ /** Set of methods that have invalid resolutions or lookups. */
+ private final Set<DexField> failedFieldResolutionTargets;
/**
* Set of program methods that are used as the bootstrap method for an invoke-dynamic instruction.
@@ -359,23 +382,32 @@
private final LambdaRewriter lambdaRewriter;
private final BackportedMethodRewriter backportRewriter;
+ private final NestBasedAccessDesugaring nestBasedAccessRewriter;
+ private final TwrCloseResourceRewriter twrCloseResourceRewriter;
+
private final DesugaredLibraryConversionWrapperAnalysis desugaredLibraryWrapperAnalysis;
private final Map<DexType, Pair<LambdaClass, ProgramMethod>> lambdaClasses =
new IdentityHashMap<>();
private final ProgramMethodMap<Map<DexCallSite, LambdaClass>> lambdaCallSites =
ProgramMethodMap.create();
private final Map<DexMethod, ProgramMethod> methodsWithBackports = new IdentityHashMap<>();
+ 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) {
+ Mode mode,
+ MainDexTracingResult previousMainDexTracingResult) {
assert appView.appServices() != null;
InternalOptions options = appView.options();
this.appInfo = appView.appInfo();
this.appView = appView.withClassHierarchy();
+ this.executorService = executorService;
this.subtypingInfo = subtypingInfo;
this.forceProguardCompatibility = options.forceProguardCompatibility;
this.graphReporter = new GraphReporter(appView, keptGraphConsumer);
@@ -384,6 +416,7 @@
this.options = options;
this.useRegistryFactory = createUseRegistryFactory();
this.workList = EnqueuerWorklist.createWorklist();
+ this.previousMainDexTracingResult = previousMainDexTracingResult;
if (mode.isInitialOrFinalTreeShaking()) {
if (options.protoShrinking().enableGeneratedMessageLiteShrinking) {
@@ -397,12 +430,19 @@
// This set is only populated in edge cases due to multiple default interface methods.
// The set is generally expected to be empty and in the unlikely chance it is not, it will
// likely contain two methods. Thus the default capacity of 2.
- failedResolutionTargets = SetUtils.newIdentityHashSet(2);
+ failedMethodResolutionTargets = SetUtils.newIdentityHashSet(2);
+ failedFieldResolutionTargets = SetUtils.newIdentityHashSet(0);
liveMethods = new LiveMethodsSet(graphReporter::registerMethod);
liveFields = new LiveFieldsSet(graphReporter::registerField);
lambdaRewriter = options.desugarState == DesugarState.ON ? new LambdaRewriter(appView) : null;
+ nestBasedAccessRewriter =
+ options.shouldDesugarNests() ? new NestBasedAccessDesugaring(appView) : null;
backportRewriter =
options.desugarState == DesugarState.ON ? new BackportedMethodRewriter(appView) : null;
+ twrCloseResourceRewriter =
+ TwrCloseResourceRewriter.enableTwrCloseResourceDesugaring(options)
+ ? new TwrCloseResourceRewriter(appView)
+ : null;
objectAllocationInfoCollection =
ObjectAllocationInfoCollectionImpl.builder(mode.isInitialTreeShaking(), graphReporter);
@@ -517,10 +557,15 @@
}
private void recordMethodReference(DexMethod method, ProgramDefinition context) {
- recordTypeReference(method.holder, context);
- recordTypeReference(method.proto.returnType, context);
+ recordMethodReference(method, context, this::reportMissingClass);
+ }
+
+ private void recordMethodReference(
+ DexMethod method, ProgramDefinition context, Consumer<DexType> missingClassConsumer) {
+ recordTypeReference(method.holder, context, missingClassConsumer);
+ recordTypeReference(method.proto.returnType, context, missingClassConsumer);
for (DexType type : method.proto.parameters.values) {
- recordTypeReference(type, context);
+ recordTypeReference(type, context, missingClassConsumer);
}
}
@@ -905,13 +950,17 @@
}
void traceCallSite(DexCallSite callSite, ProgramMethod context) {
- DexProgramClass bootstrapClass =
- getProgramHolderOrNull(callSite.bootstrapMethod.asMethod(), context);
- if (bootstrapClass != null) {
- bootstrapMethods.add(callSite.bootstrapMethod.asMethod());
+ // Do not lookup java.lang.invoke.LambdaMetafactory when compiling for DEX to avoid reporting
+ // the class as missing.
+ if (options.isGeneratingClassFiles() || !isLambdaMetafactoryMethod(callSite, appInfo())) {
+ DexProgramClass bootstrapClass =
+ getProgramHolderOrNull(callSite.bootstrapMethod.asMethod(), context);
+ if (bootstrapClass != null) {
+ bootstrapMethods.add(callSite.bootstrapMethod.asMethod());
+ }
}
- LambdaDescriptor descriptor = LambdaDescriptor.tryInfer(callSite, appInfo, context);
+ LambdaDescriptor descriptor = LambdaDescriptor.tryInfer(callSite, appInfo(), context);
if (descriptor == null) {
return;
}
@@ -921,7 +970,7 @@
assert contextMethod.getCode().isCfCode() : "Unexpected input type with lambdas";
CfCode code = contextMethod.getCode().asCfCode();
if (code != null) {
- LambdaClass lambdaClass = lambdaRewriter.getOrCreateLambdaClass(descriptor, context);
+ LambdaClass lambdaClass = lambdaRewriter.createLambdaClass(descriptor, context);
lambdaClasses.put(lambdaClass.type, new Pair<>(lambdaClass, context));
lambdaCallSites
.computeIfAbsent(context, k -> new IdentityHashMap<>())
@@ -1234,12 +1283,24 @@
return false;
}
+ private boolean registerCloseResource(DexMethod invokedMethod, ProgramMethod context) {
+ if (twrCloseResourceRewriter != null
+ && TwrCloseResourceRewriter.isTwrCloseResourceMethod(
+ invokedMethod, appView.dexItemFactory())) {
+ methodsWithTwrCloseResource.putIfAbsent(context.getReference(), context);
+ return true;
+ }
+ return false;
+ }
+
private void traceInvokeStatic(
DexMethod invokedMethod, ProgramMethod context, KeepReason reason) {
if (registerBackportInvoke(invokedMethod, context)) {
return;
}
-
+ if (registerCloseResource(invokedMethod, context)) {
+ return;
+ }
DexItemFactory dexItemFactory = appView.dexItemFactory();
if (dexItemFactory.classMethods.isReflectiveClassLookup(invokedMethod)
|| dexItemFactory.atomicFieldUpdaterMethods.isFieldUpdater(invokedMethod)) {
@@ -1659,18 +1720,38 @@
return;
}
+ assert !mode.isFinalMainDexTracing()
+ || !options.testing.checkForNotExpandingMainDexTracingResult
+ || previousMainDexTracingResult.isRoot(clazz)
+ || clazz.toSourceString().contains(ENUM_UNBOXING_UTILITY_CLASS_SUFFIX)
+ // TODO(b/177847090): Consider not outlining anything in main dex.
+ || clazz.toSourceString().contains(OutlineOptions.CLASS_NAME)
+ : "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);
}
+
+ // Mark types in nest attributes referenced.
+ if (clazz.isNestHost()) {
+ for (NestMemberClassAttribute nestMemberClassAttribute :
+ clazz.getNestMembersClassAttributes()) {
+ recordTypeReference(nestMemberClassAttribute.getNestMember(), clazz);
+ }
+ } else {
+ recordTypeReference(clazz.getNestHost(), clazz);
+ }
+
EnclosingMethodAttribute enclosingMethodAttribute = clazz.getEnclosingMethodAttribute();
if (enclosingMethodAttribute != null) {
DexMethod enclosingMethod = enclosingMethodAttribute.getEnclosingMethod();
if (enclosingMethod != null) {
- recordMethodReference(enclosingMethod, clazz);
+ recordMethodReference(enclosingMethod, clazz, this::ignoreMissingClass);
} else {
- recordTypeReference(enclosingMethodAttribute.getEnclosingClass(), clazz);
+ recordTypeReference(
+ enclosingMethodAttribute.getEnclosingClass(), clazz, this::ignoreMissingClass);
}
}
@@ -1680,7 +1761,7 @@
KeepReason reason = KeepReason.reachableFromLiveType(clazz.type);
- for (DexType iface : clazz.interfaces.values) {
+ for (DexType iface : clazz.getInterfaces()) {
markInterfaceTypeAsLiveViaInheritanceClause(iface, clazz);
}
@@ -1766,33 +1847,40 @@
if (!appView.options().enableUnusedInterfaceRemoval
|| rootSet.noUnusedInterfaceRemoval.contains(type)
- || mode.isTracingMainDex()) {
+ || mode.isMainDexTracing()) {
markTypeAsLive(clazz, implementer);
- } else {
- if (liveTypes.contains(clazz)) {
- // The interface is already live, so make sure to report this implements-edge.
- graphReporter.reportClassReferencedFrom(clazz, implementer);
- } else {
- // No need to mark the type as live. If an interface type is only reachable via the
- // inheritance clause of another type it can simply be removed from the inheritance clause.
- // The interface is needed if it has a live default interface method or field, though.
- // Therefore, we record that this implemented-by edge has not been reported, such that we
- // can report it in the future if one its members becomes live.
- WorkList<DexProgramClass> worklist = WorkList.newIdentityWorkList();
- worklist.addIfNotSeen(clazz);
- while (worklist.hasNext()) {
- DexProgramClass current = worklist.next();
- if (liveTypes.contains(current)) {
- continue;
- }
- Set<DexProgramClass> implementors =
- unusedInterfaceTypes.computeIfAbsent(current, ignore -> Sets.newIdentityHashSet());
- if (implementors.add(implementer)) {
- for (DexType iface : current.interfaces.values) {
- DexProgramClass definition = getProgramClassOrNull(iface, current);
- if (definition != null) {
- worklist.addIfNotSeen(definition);
- }
+ return;
+ }
+
+ if (liveTypes.contains(clazz)) {
+ // The interface is already live, so make sure to report this implements-edge.
+ graphReporter.reportClassReferencedFrom(clazz, implementer);
+ return;
+ }
+
+ // No need to mark the type as live. If an interface type is only reachable via the
+ // inheritance clause of another type it can simply be removed from the inheritance clause.
+ // The interface is needed if it has a live default interface method or field, though.
+ // Therefore, we record that this implemented-by edge has not been reported, such that we
+ // can report it in the future if one its members becomes live.
+ WorkList<DexProgramClass> worklist = WorkList.newIdentityWorkList();
+ worklist.addIfNotSeen(clazz);
+ while (worklist.hasNext()) {
+ DexProgramClass current = worklist.next();
+ if (liveTypes.contains(current)) {
+ continue;
+ }
+ Set<DexProgramClass> implementors =
+ unusedInterfaceTypes.computeIfAbsent(current, ignore -> Sets.newIdentityHashSet());
+ if (implementors.add(implementer)) {
+ for (DexType iface : current.getInterfaces()) {
+ DexProgramClass definition = getProgramClassOrNull(iface, current);
+ if (definition != null) {
+ if (definition.isPublic()
+ || implementer.getType().isSamePackage(definition.getType())) {
+ worklist.addIfNotSeen(definition);
+ } else {
+ markTypeAsLive(current, implementer);
}
}
}
@@ -1859,6 +1947,7 @@
FieldResolutionResult resolutionResult = appInfo.resolveField(field);
if (resolutionResult.isFailedOrUnknownResolution()) {
reportMissingField(field);
+ failedFieldResolutionTargets.add(field);
}
return resolutionResult;
}
@@ -1870,7 +1959,8 @@
ResolutionResult resolutionResult = appInfo.unsafeResolveMethodDueToDexFormat(method);
if (resolutionResult.isFailedResolution()) {
reportMissingMethod(method);
- markFailedResolutionTargets(method, resolutionResult.asFailedResolution(), context, reason);
+ markFailedMethodResolutionTargets(
+ method, resolutionResult.asFailedResolution(), context, reason);
}
return resolutionResult.asSingleResolution();
}
@@ -1882,7 +1972,8 @@
ResolutionResult resolutionResult = appInfo.resolveMethod(method, interfaceInvoke);
if (resolutionResult.isFailedResolution()) {
reportMissingMethod(method);
- markFailedResolutionTargets(method, resolutionResult.asFailedResolution(), context, reason);
+ markFailedMethodResolutionTargets(
+ method, resolutionResult.asFailedResolution(), context, reason);
}
return resolutionResult.asSingleResolution();
}
@@ -2020,6 +2111,7 @@
// TODO(zerny): Is it ok that we lookup in both the direct and virtual pool here?
DexEncodedMethod encodedMethod = clazz.lookupMethod(reference);
if (encodedMethod == null) {
+ failedMethodResolutionTargets.add(reference);
reportMissingMethod(reference);
return;
}
@@ -2047,7 +2139,7 @@
}
private void ensureFromLibraryOrThrow(DexType type, DexLibraryClass context) {
- if (mode.isTracingMainDex()) {
+ if (mode.isMainDexTracing()) {
// b/72312389: android.jar contains parts of JUnit and most developers include JUnit in
// their programs. This leads to library classes extending program classes. When tracing
// main dex lists we allow this.
@@ -2071,7 +2163,7 @@
}
});
}
- if (dontWarnPatterns.matches(context.type)) {
+ if (appView.getDontWarnConfiguration().matches(context)) {
// Ignore.
return;
}
@@ -2245,20 +2337,24 @@
private void checkLambdaInterface(DexType itf, ProgramMethod context) {
DexClass clazz = definitionFor(itf, context);
if (clazz == null) {
- StringDiagnostic message =
- new StringDiagnostic(
- "Lambda expression implements missing interface `" + itf.toSourceString() + "`",
- context.getOrigin());
- options.reporter.warning(message);
+ if (!appView.getDontWarnConfiguration().matches(itf)) {
+ StringDiagnostic message =
+ new StringDiagnostic(
+ "Lambda expression implements missing interface `" + itf.toSourceString() + "`",
+ context.getOrigin());
+ options.reporter.warning(message);
+ }
} else if (!clazz.isInterface()) {
- StringDiagnostic message =
- new StringDiagnostic(
- "Lambda expression expected to implement an interface, but found "
- + "`"
- + itf.toSourceString()
- + "`",
- context.getOrigin());
- options.reporter.warning(message);
+ if (!appView.getDontWarnConfiguration().matches(itf)) {
+ StringDiagnostic message =
+ new StringDiagnostic(
+ "Lambda expression expected to implement an interface, but found "
+ + "`"
+ + itf.toSourceString()
+ + "`",
+ context.getOrigin());
+ options.reporter.warning(message);
+ }
}
}
@@ -2385,7 +2481,7 @@
private void markLibraryAndClasspathMethodOverridesAsLive(
InstantiatedObject instantiation, DexClass libraryClass) {
assert libraryClass.isNotProgramClass();
- if (mode.isTracingMainDex()) {
+ if (mode.isMainDexTracing()) {
// Library roots must be specified for tracing of library methods. For classpath the expected
// use case is that the classes will be classloaded, thus they should have no bearing on the
// content of the main dex file.
@@ -2804,17 +2900,17 @@
}
}
- private void markFailedResolutionTargets(
+ private void markFailedMethodResolutionTargets(
DexMethod symbolicMethod,
FailedResolutionResult failedResolution,
ProgramDefinition context,
KeepReason reason) {
- failedResolutionTargets.add(symbolicMethod);
+ failedMethodResolutionTargets.add(symbolicMethod);
failedResolution.forEachFailureDependency(
method -> {
DexProgramClass clazz = getProgramClassOrNull(method.getHolderType(), context);
if (clazz != null) {
- failedResolutionTargets.add(method.method);
+ failedMethodResolutionTargets.add(method.method);
markMethodAsTargeted(new ProgramMethod(clazz, method), reason);
}
});
@@ -2860,7 +2956,7 @@
// If invoke target is invalid (inaccessible or not an instance-method) record it and stop.
DexClassAndMethod target = resolution.lookupInvokeSuperTarget(from.getHolder(), appInfo);
if (target == null) {
- failedResolutionTargets.add(resolution.getResolvedMethod().method);
+ failedMethodResolutionTargets.add(resolution.getResolvedMethod().method);
return;
}
@@ -2891,7 +2987,7 @@
public Set<DexProgramClass> traceMainDex(
RootSet rootSet, ExecutorService executorService, Timing timing) throws ExecutionException {
assert analyses.isEmpty();
- assert mode.isTracingMainDex();
+ assert mode.isMainDexTracing();
this.rootSet = rootSet;
// Translate the result of root-set computation into enqueuer actions.
enqueueRootItems(rootSet.noShrinking);
@@ -2902,12 +2998,10 @@
public AppInfoWithLiveness traceApplication(
RootSet rootSet,
- ProguardClassFilter dontWarnPatterns,
ExecutorService executorService,
Timing timing)
throws ExecutionException {
this.rootSet = rootSet;
- this.dontWarnPatterns = dontWarnPatterns;
// Translate the result of root-set computation into enqueuer actions.
if (appView.options().getProguardConfiguration() != null
&& !options.kotlinOptimizationOptions().disableKotlinSpecificOptimizations) {
@@ -3012,6 +3106,8 @@
private static class SyntheticAdditions {
+ List<ProgramMethod> desugaredMethods = new LinkedList<>();
+
Map<DexType, Pair<DexProgramClass, ProgramMethod>> syntheticInstantiations =
new IdentityHashMap<>();
@@ -3020,6 +3116,8 @@
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.
@@ -3031,20 +3129,18 @@
boolean isEmpty() {
boolean empty =
- syntheticInstantiations.isEmpty()
+ desugaredMethods.isEmpty()
+ && syntheticInstantiations.isEmpty()
&& liveMethods.isEmpty()
+ && syntheticProgramClasses.isEmpty()
&& syntheticClasspathClasses.isEmpty();
assert !empty || (liveMethodsWithKeepActions.isEmpty() && mainDexTypes.isEmpty());
return empty;
}
- void addInstantiatedClass(
- DexProgramClass clazz, ProgramMethod context, boolean isMainDexClass) {
+ void addInstantiatedClass(DexProgramClass clazz, ProgramMethod context) {
assert !syntheticInstantiations.containsKey(clazz.type);
syntheticInstantiations.put(clazz.type, new Pair<>(clazz, context));
- if (isMainDexClass) {
- mainDexTypes.add(clazz);
- }
}
void addClasspathClass(DexClasspathClass clazz) {
@@ -3052,6 +3148,11 @@
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);
@@ -3066,10 +3167,7 @@
void amendApplication(Builder appBuilder) {
assert !isEmpty();
- for (Pair<DexProgramClass, ProgramMethod> clazzAndContext :
- syntheticInstantiations.values()) {
- appBuilder.addProgramClass(clazzAndContext.getFirst());
- }
+ appBuilder.addProgramClasses(syntheticProgramClasses.values());
appBuilder.addClasspathClasses(syntheticClasspathClasses.values());
}
@@ -3081,9 +3179,14 @@
void enqueueWorkItems(Enqueuer enqueuer) {
assert !isEmpty();
assert enqueuer.mode.isInitialTreeShaking();
+
// All synthetic additions are initial tree shaking only. No need to track keep reasons.
KeepReasonWitness fakeReason = enqueuer.graphReporter.fakeReportShouldNotBeUsed();
+ for (ProgramMethod desugaredMethod : desugaredMethods) {
+ enqueuer.workList.enqueueTraceCodeAction(desugaredMethod);
+ }
+
liveMethodsWithKeepActions.forEach(
item -> enqueuer.keepInfo.joinMethod(item.getFirst(), item.getSecond()));
for (Pair<DexProgramClass, ProgramMethod> clazzAndContext :
@@ -3127,7 +3230,7 @@
}
}
- private void synthesize() {
+ private void synthesize() throws ExecutionException {
if (!mode.isInitialTreeShaking()) {
return;
}
@@ -3135,11 +3238,14 @@
// In particular these additions are order independent, i.e., it does not matter which are
// registered first and no dependencies may exist among them.
SyntheticAdditions additions = new SyntheticAdditions();
+ desugar(additions);
synthesizeInvokeSpecialBridges(additions);
synthesizeInterfaceMethodBridges(additions);
synthesizeLambdas(additions);
synthesizeLibraryConversionWrappers(additions);
synthesizeBackports(additions);
+ synthesizeNestConstructor(additions);
+ synthesizeTwrCloseResource(additions);
if (additions.isEmpty()) {
return;
}
@@ -3161,6 +3267,50 @@
additions.enqueueWorkItems(this);
}
+ private void desugar(SyntheticAdditions additions) throws ExecutionException {
+ ThreadUtils.processItems(pendingDesugaring, this::desugar, executorService);
+ Iterables.addAll(additions.desugaredMethods, pendingDesugaring);
+ pendingDesugaring.clear();
+ }
+
+ private void desugar(ProgramMethod method) {
+ Code code = method.getDefinition().getCode();
+ if (!code.isCfCode()) {
+ appView
+ .options()
+ .reporter
+ .error(
+ new StringDiagnostic(
+ "Unsupported attempt to desugar non-CF code",
+ method.getOrigin(),
+ method.getPosition()));
+ return;
+ }
+
+ CfCode cfCode = code.asCfCode();
+ List<CfInstruction> desugaredInstructions =
+ ListUtils.flatMap(
+ cfCode.getInstructions(),
+ instruction -> {
+ // TODO(b/177810578): Migrate other cf-to-cf based desugaring here, and assert that
+ // that at most one instruction desugarer applies to each instruction.
+ if (nestBasedAccessRewriter != null) {
+ List<CfInstruction> replacement =
+ nestBasedAccessRewriter.desugarInstruction(instruction, method, null);
+ if (replacement != null) {
+ return replacement;
+ }
+ }
+ return null;
+ },
+ null);
+ if (desugaredInstructions != null) {
+ cfCode.setInstructions(desugaredInstructions);
+ } else {
+ assert false : "Expected code to be desugared";
+ }
+ }
+
private void synthesizeInvokeSpecialBridges(SyntheticAdditions additions) {
SortedProgramMethodSet bridges =
appView.getInvokeSpecialBridgeSynthesizer().insertBridgesForR8();
@@ -3183,6 +3333,22 @@
}
}
+ 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);
+ }
+ }
+
private void synthesizeLambdas(SyntheticAdditions additions) {
if (lambdaRewriter == null || lambdaClasses.isEmpty()) {
assert lambdaCallSites.isEmpty();
@@ -3193,8 +3359,8 @@
// Add all desugared classes to the application, main-dex list, and mark them instantiated.
LambdaClass lambdaClass = lambdaClassAndContext.getFirst();
ProgramMethod context = lambdaClassAndContext.getSecond();
- DexProgramClass programClass = lambdaClass.getOrCreateLambdaClass();
- additions.addInstantiatedClass(programClass, context, lambdaClass.addToMainDexList.get());
+ DexProgramClass programClass = lambdaClass.getLambdaProgramClass();
+ additions.addInstantiatedClass(programClass, context);
// Mark the instance constructor targeted and live.
DexEncodedMethod constructor = programClass.lookupDirectMethod(lambdaClass.constructor);
KeepReason reason = KeepReason.instantiatedIn(context);
@@ -3299,11 +3465,12 @@
appInfo.getMainDexClasses(),
deadProtoTypes,
appView.testing().enableExperimentalMissingClassesReporting
- ? missingClassesBuilder.reportMissingClasses(options)
+ ? missingClassesBuilder.reportMissingClasses(appView)
: missingClassesBuilder.ignoreMissingClasses(),
SetUtils.mapIdentityHashSet(liveTypes.getItems(), DexProgramClass::getType),
Enqueuer.toDescriptorSet(targetedMethods.getItems()),
- Collections.unmodifiableSet(failedResolutionTargets),
+ Collections.unmodifiableSet(failedMethodResolutionTargets),
+ Collections.unmodifiableSet(failedFieldResolutionTargets),
Collections.unmodifiableSet(bootstrapMethods),
Collections.unmodifiableSet(methodsTargetedByInvokeDynamic),
Collections.unmodifiableSet(virtualMethodsTargetedByInvokeDirect),
@@ -3349,10 +3516,9 @@
lambdaRewriter
.getKnownLambdaClasses()
.forEach(
- (type, lambda) -> {
- DexProgramClass synthesizedClass = lambda.getOrCreateLambdaClass();
+ lambda -> {
+ DexProgramClass synthesizedClass = lambda.getLambdaProgramClass();
assert synthesizedClass != null;
- assert synthesizedClass == appInfo().definitionForWithoutExistenceAssert(type);
assert liveTypes.contains(synthesizedClass);
if (synthesizedClass == null) {
return;
@@ -3835,7 +4001,8 @@
processAnnotations(holder, method);
definition.parameterAnnotationsList.forEachAnnotation(
annotation -> processAnnotation(holder, method, annotation));
- method.registerCodeReferences(useRegistryFactory.create(appView, method, this));
+
+ traceNonDesugaredCode(method);
// Add all dependent members to the workqueue.
enqueueRootItems(rootSet.getDependentItems(definition));
@@ -3846,6 +4013,21 @@
analyses.forEach(analysis -> analysis.processNewlyLiveMethod(method, context));
}
+ private void traceNonDesugaredCode(ProgramMethod method) {
+ if (getMode().isInitialTreeShaking()) {
+ if (nestBasedAccessRewriter != null && nestBasedAccessRewriter.needsDesugaring(method)) {
+ pendingDesugaring.add(method);
+ return;
+ }
+ }
+
+ traceCode(method);
+ }
+
+ void traceCode(ProgramMethod method) {
+ method.registerCodeReferences(useRegistryFactory.create(appView, method, this));
+ }
+
private void checkMemberForSoftPinning(ProgramMember<?, ?> member) {
DexMember<?, ?> reference = member.getDefinition().getReference();
Set<ProguardKeepRuleBase> softPinRules = rootSet.softPinned.getRulesForReference(reference);
@@ -4510,7 +4692,7 @@
}
public DexClass definitionFor(DexType type, ProgramDefinition context) {
- return enqueuer.definitionFor(type, context);
+ return enqueuer.definitionFor(type, context, enqueuer::ignoreMissingClass);
}
}
}
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 34a8750..04fb840 100644
--- a/src/main/java/com/android/tools/r8/shaking/EnqueuerFactory.java
+++ b/src/main/java/com/android/tools/r8/shaking/EnqueuerFactory.java
@@ -11,45 +11,82 @@
import com.android.tools.r8.graph.SubtypingInfo;
import com.android.tools.r8.shaking.Enqueuer.Mode;
import java.util.Set;
+import java.util.concurrent.ExecutorService;
public class EnqueuerFactory {
public static Enqueuer createForInitialTreeShaking(
AppView<? extends AppInfoWithClassHierarchy> appView,
+ ExecutorService executorService,
SubtypingInfo subtypingInfo) {
- return new Enqueuer(appView, subtypingInfo, null, Mode.INITIAL_TREE_SHAKING);
+ return new Enqueuer(
+ appView,
+ executorService,
+ subtypingInfo,
+ null,
+ Mode.INITIAL_TREE_SHAKING,
+ MainDexTracingResult.NONE);
}
public static Enqueuer createForFinalTreeShaking(
AppView<? extends AppInfoWithClassHierarchy> appView,
+ ExecutorService executorService,
SubtypingInfo subtypingInfo,
GraphConsumer keptGraphConsumer,
Set<DexType> initialPrunedTypes) {
Enqueuer enqueuer =
- new Enqueuer(appView, subtypingInfo, keptGraphConsumer, Mode.FINAL_TREE_SHAKING);
+ new Enqueuer(
+ appView,
+ executorService,
+ subtypingInfo,
+ keptGraphConsumer,
+ Mode.FINAL_TREE_SHAKING,
+ MainDexTracingResult.NONE);
appView.withProtoShrinker(
shrinker -> enqueuer.setInitialDeadProtoTypes(shrinker.getDeadProtoTypes()));
enqueuer.setInitialPrunedTypes(initialPrunedTypes);
return enqueuer;
}
- public static Enqueuer createForMainDexTracing(
+ public static Enqueuer createForInitialMainDexTracing(
AppView<? extends AppInfoWithClassHierarchy> appView,
+ ExecutorService executorService,
SubtypingInfo subtypingInfo) {
- return createForMainDexTracing(appView, subtypingInfo, null);
+ return new Enqueuer(
+ appView,
+ executorService,
+ subtypingInfo,
+ null,
+ Mode.INITIAL_MAIN_DEX_TRACING,
+ MainDexTracingResult.NONE);
}
- public static Enqueuer createForMainDexTracing(
+ public static Enqueuer createForFinalMainDexTracing(
AppView<? extends AppInfoWithClassHierarchy> appView,
+ ExecutorService executorService,
SubtypingInfo subtypingInfo,
- GraphConsumer keptGraphConsumer) {
- return new Enqueuer(appView, subtypingInfo, keptGraphConsumer, Mode.MAIN_DEX_TRACING);
+ GraphConsumer keptGraphConsumer,
+ MainDexTracingResult previousMainDexTracingResult) {
+ return new Enqueuer(
+ appView,
+ executorService,
+ subtypingInfo,
+ keptGraphConsumer,
+ Mode.FINAL_MAIN_DEX_TRACING,
+ previousMainDexTracingResult);
}
public static Enqueuer createForWhyAreYouKeeping(
AppView<? extends AppInfoWithClassHierarchy> appView,
+ ExecutorService executorService,
SubtypingInfo subtypingInfo,
GraphConsumer keptGraphConsumer) {
- return new Enqueuer(appView, subtypingInfo, keptGraphConsumer, Mode.WHY_ARE_YOU_KEEPING);
+ return new Enqueuer(
+ appView,
+ executorService,
+ subtypingInfo,
+ keptGraphConsumer,
+ Mode.WHY_ARE_YOU_KEEPING,
+ MainDexTracingResult.NONE);
}
}
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 0be900c..b403b54 100644
--- a/src/main/java/com/android/tools/r8/shaking/EnqueuerWorklist.java
+++ b/src/main/java/com/android/tools/r8/shaking/EnqueuerWorklist.java
@@ -174,6 +174,19 @@
}
}
+ static class TraceCodeAction extends EnqueuerAction {
+ private final ProgramMethod method;
+
+ TraceCodeAction(ProgramMethod method) {
+ this.method = method;
+ }
+
+ @Override
+ public void run(Enqueuer enqueuer) {
+ enqueuer.traceCode(method);
+ }
+ }
+
static class TraceConstClassAction extends EnqueuerAction {
private final DexType type;
// TODO(b/175854431): Avoid pushing context on worklist.
@@ -304,6 +317,10 @@
queue.add(new MarkFieldKeptAction(field, witness));
}
+ public void enqueueTraceCodeAction(ProgramMethod method) {
+ queue.add(new TraceCodeAction(method));
+ }
+
public void enqueueTraceConstClassAction(DexType type, ProgramMethod context) {
queue.add(new TraceConstClassAction(type, context));
}
diff --git a/src/main/java/com/android/tools/r8/shaking/GraphReporter.java b/src/main/java/com/android/tools/r8/shaking/GraphReporter.java
index 86b0d08..6459b7f 100644
--- a/src/main/java/com/android/tools/r8/shaking/GraphReporter.java
+++ b/src/main/java/com/android/tools/r8/shaking/GraphReporter.java
@@ -423,7 +423,7 @@
}
GraphEdgeInfo getEdgeInfo(EdgeKind kind) {
- return reasonInfo.computeIfAbsent(kind, k -> new GraphEdgeInfo(k));
+ return reasonInfo.computeIfAbsent(kind, GraphEdgeInfo::new);
}
private DexClass definitionFor(DexType type) {
diff --git a/src/main/java/com/android/tools/r8/shaking/MainDexTracingResult.java b/src/main/java/com/android/tools/r8/shaking/MainDexTracingResult.java
index 3af323c..5bf287f 100644
--- a/src/main/java/com/android/tools/r8/shaking/MainDexTracingResult.java
+++ b/src/main/java/com/android/tools/r8/shaking/MainDexTracingResult.java
@@ -146,6 +146,10 @@
return getRoots().contains(definition.getContextType());
}
+ public boolean isRoot(DexType type) {
+ return getRoots().contains(type);
+ }
+
public boolean isDependency(ProgramDefinition definition) {
return getDependencies().contains(definition.getContextType());
}
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 d3cd164..e296cd7 100644
--- a/src/main/java/com/android/tools/r8/shaking/MissingClasses.java
+++ b/src/main/java/com/android/tools/r8/shaking/MissingClasses.java
@@ -4,6 +4,7 @@
package com.android.tools.r8.shaking;
+import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.synthesis.CommittedItems;
@@ -90,11 +91,13 @@
return build();
}
- public MissingClasses reportMissingClasses(InternalOptions options) {
+ public MissingClasses reportMissingClasses(AppView<?> appView) {
+ InternalOptions options = appView.options();
Set<DexType> newMissingClassesWithoutDontWarn =
- options.getProguardConfiguration().getDontWarnPatterns().getNonMatches(newMissingClasses);
+ appView.getDontWarnConfiguration().getNonMatches(newMissingClasses);
+ newMissingClassesWithoutDontWarn.removeAll(alreadyMissingClasses);
newMissingClassesWithoutDontWarn.removeAll(
- getAllowedMissingClasses(options.dexItemFactory()));
+ getAllowedMissingClasses(appView.dexItemFactory()));
if (!newMissingClassesWithoutDontWarn.isEmpty()) {
MissingClassesDiagnostic diagnostic =
new MissingClassesDiagnostic.Builder()
@@ -111,10 +114,18 @@
}
private static Collection<DexType> getAllowedMissingClasses(DexItemFactory dexItemFactory) {
- return ImmutableList.of(dexItemFactory.annotationThrows);
+ 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);
}
- /** Intentionally private, use {@link Builder#reportMissingClasses(InternalOptions)}. */
+ /** Intentionally private, use {@link Builder#reportMissingClasses(AppView)}. */
private MissingClasses build() {
// Extend the newMissingClasses set with all other missing classes.
//
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardClassFilter.java b/src/main/java/com/android/tools/r8/shaking/ProguardClassFilter.java
index 88c13ec..2580817 100644
--- a/src/main/java/com/android/tools/r8/shaking/ProguardClassFilter.java
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardClassFilter.java
@@ -4,14 +4,9 @@
package com.android.tools.r8.shaking;
-import static com.android.tools.r8.utils.PredicateUtils.not;
-
import com.android.tools.r8.graph.DexType;
-import com.android.tools.r8.shaking.ProguardTypeMatcher.MatchSpecificType;
-import com.android.tools.r8.utils.TraversalContinuation;
import com.google.common.collect.ImmutableList;
-import com.google.common.collect.Sets;
-import java.util.Set;
+import java.util.List;
public class ProguardClassFilter {
private static ProguardClassFilter EMPTY = new ProguardClassFilter(ImmutableList.of());
@@ -46,6 +41,10 @@
return EMPTY;
}
+ public List<ProguardClassNameList> getPatterns() {
+ return patterns;
+ }
+
public boolean isEmpty() {
return patterns.size() == 0;
}
@@ -58,47 +57,4 @@
}
return false;
}
-
- public Set<DexType> getNonMatches(Set<DexType> types) {
- Set<DexType> nonMatches = Sets.newIdentityHashSet();
- for (DexType type : types) {
- TraversalContinuation traversalContinuation = TraversalContinuation.CONTINUE;
- for (ProguardClassNameList pattern : patterns) {
- traversalContinuation =
- pattern.traverseTypeMatchers(
- matcher -> {
- if (matcher.matches(type)) {
- return TraversalContinuation.BREAK;
- }
- return TraversalContinuation.CONTINUE;
- },
- not(ProguardTypeMatcher::hasSpecificType));
- if (traversalContinuation.shouldBreak()) {
- break;
- }
- }
- if (traversalContinuation.shouldContinue()) {
- nonMatches.add(type);
- }
- }
- for (ProguardClassNameList pattern : patterns) {
- pattern.forEachTypeMatcher(
- matcher -> nonMatches.remove(matcher.getSpecificType()),
- ProguardTypeMatcher::hasSpecificType);
- }
- return nonMatches;
- }
-
- public void filterOutMatches(Set<DexType> types) {
- for (ProguardClassNameList pattern : patterns) {
- pattern.forEachTypeMatcher(matcher -> {
- if (matcher instanceof MatchSpecificType) {
- assert matcher.getSpecificType() != null;
- types.remove(matcher.getSpecificType());
- } else {
- types.removeIf(matcher::matches);
- }
- });
- }
- }
}
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardConfiguration.java b/src/main/java/com/android/tools/r8/shaking/ProguardConfiguration.java
index 7d8d88a..8fdd330 100644
--- a/src/main/java/com/android/tools/r8/shaking/ProguardConfiguration.java
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardConfiguration.java
@@ -3,6 +3,7 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.shaking;
+import com.android.tools.r8.errors.dontwarn.DontWarnConfiguration;
import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.naming.DictionaryReader;
import com.android.tools.r8.origin.Origin;
@@ -627,7 +628,12 @@
return keepPackageNamesPatterns;
}
- public ProguardClassFilter getDontWarnPatterns() {
+ public boolean hasDontWarnPatterns() {
+ return !dontWarnPatterns.isEmpty();
+ }
+
+ public ProguardClassFilter getDontWarnPatterns(DontWarnConfiguration.Witness witness) {
+ assert witness != null;
return dontWarnPatterns;
}
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationParser.java b/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationParser.java
index 47ac1e3..49e9f1e 100644
--- a/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationParser.java
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationParser.java
@@ -40,6 +40,7 @@
import java.util.List;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
+import java.util.function.IntPredicate;
import java.util.function.Predicate;
public class ProguardConfigurationParser {
@@ -1726,25 +1727,25 @@
return Integer.parseInt(s);
}
- private final Predicate<Integer> CLASS_NAME_PREDICATE =
- codePoint ->
- IdentifierUtils.isDexIdentifierPart(codePoint)
- || codePoint == '.'
- || codePoint == '*'
- || codePoint == '?'
- || codePoint == '%'
- || codePoint == '['
- || codePoint == ']';
+ private boolean isClassName(int codePoint) {
+ return IdentifierUtils.isDexIdentifierPart(codePoint)
+ || codePoint == '.'
+ || codePoint == '*'
+ || codePoint == '?'
+ || codePoint == '%'
+ || codePoint == '['
+ || codePoint == ']';
+ }
- private final Predicate<Integer> PACKAGE_NAME_PREDICATE =
- codePoint ->
- IdentifierUtils.isDexIdentifierPart(codePoint)
- || codePoint == '.'
- || codePoint == '*'
- || codePoint == '?';
+ private boolean isPackageName(int codePoint) {
+ return IdentifierUtils.isDexIdentifierPart(codePoint)
+ || codePoint == '.'
+ || codePoint == '*'
+ || codePoint == '?';
+ }
private String acceptClassName() {
- return acceptString(CLASS_NAME_PREDICATE);
+ return acceptString(this::isClassName);
}
private IdentifierPatternWithWildcards acceptIdentifierWithBackreference(IdentifierType kind) {
@@ -1842,8 +1843,8 @@
wildcardsCollector.add(new ProguardWildcard.Pattern(String.valueOf((char) current)));
end += Character.charCount(current);
} else if (kind == IdentifierType.PACKAGE_NAME
- ? PACKAGE_NAME_PREDICATE.test(current)
- : (CLASS_NAME_PREDICATE.test(current) || current == '>')) {
+ ? isPackageName(current)
+ : (isClassName(current) || current == '>')) {
end += Character.charCount(current);
} else if (kind != IdentifierType.PACKAGE_NAME && current == '<') {
currentBackreference = new StringBuilder();
@@ -1942,7 +1943,7 @@
|| codePoint == '.');
}
- private String acceptString(Predicate<Integer> codepointAcceptor) {
+ private String acceptString(IntPredicate codepointAcceptor) {
int start = position;
int end = position;
while (!eof(end)) {
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationUtils.java b/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationUtils.java
index 42b56bd..c6afc11 100644
--- a/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationUtils.java
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationUtils.java
@@ -134,11 +134,15 @@
if (rule.getClassType() != ProguardClassType.CLASS) {
continue;
}
+ if (!rule.getClassAnnotations().isEmpty() || !rule.getInheritanceAnnotations().isEmpty()) {
+ continue;
+ }
if (rule.hasInheritanceClassName()
&& !rule.getInheritanceClassName().matches(factory.objectType)) {
continue;
}
- if (!rule.getClassNames().matches(factory.androidOsBuildVersionType)) {
+ if (rule.getClassNames().hasWildcards()
+ || !rule.getClassNames().matches(factory.androidOsBuildVersionType)) {
continue;
}
for (ProguardMemberRule memberRule : rule.getMemberRules()) {
@@ -149,6 +153,9 @@
if (memberRule.getRuleType() != ProguardMemberType.FIELD) {
continue;
}
+ if (!memberRule.getAnnotations().isEmpty()) {
+ continue;
+ }
if (memberRule.getAccessFlags().isProtected()
|| memberRule.getAccessFlags().isPrivate()
|| memberRule.getAccessFlags().isAbstract()
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardMemberRuleReturnValue.java b/src/main/java/com/android/tools/r8/shaking/ProguardMemberRuleReturnValue.java
index c8545c7..11b8868 100644
--- a/src/main/java/com/android/tools/r8/shaking/ProguardMemberRuleReturnValue.java
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardMemberRuleReturnValue.java
@@ -60,6 +60,10 @@
return type == Type.FIELD;
}
+ public boolean isNonNull() {
+ return isValueRange() && getValueRange().getMin() > 0;
+ }
+
public boolean isNull() {
return type == Type.NULL;
}
diff --git a/src/main/java/com/android/tools/r8/shaking/RootSetBuilder.java b/src/main/java/com/android/tools/r8/shaking/RootSetBuilder.java
index 7f18fff..33bfc85 100644
--- a/src/main/java/com/android/tools/r8/shaking/RootSetBuilder.java
+++ b/src/main/java/com/android/tools/r8/shaking/RootSetBuilder.java
@@ -1727,11 +1727,7 @@
}
private void generateAssumeNoSideEffectsWarnings() {
- ProguardClassFilter dontWarnPatterns =
- options.getProguardConfiguration() != null
- ? options.getProguardConfiguration().getDontWarnPatterns()
- : ProguardClassFilter.empty();
- if (dontWarnPatterns.matches(options.itemFactory.objectType)) {
+ if (appView.getDontWarnConfiguration().matches(options.itemFactory.objectType)) {
// Don't report any warnings since we don't apply -assumenosideeffects rules to notify() or
// wait() anyway.
return;
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 e049766..c52fd0f 100644
--- a/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java
+++ b/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java
@@ -294,7 +294,7 @@
}
// The set of targets that must remain for proper resolution error cases should not be merged.
- for (DexMethod method : appInfo.getFailedResolutionTargets()) {
+ for (DexMethod method : appInfo.getFailedMethodResolutionTargets()) {
markTypeAsPinned(method.holder, AbortReason.RESOLUTION_FOR_METHODS_MAY_CHANGE);
}
}
diff --git a/src/main/java/com/android/tools/r8/synthesis/CommittedItems.java b/src/main/java/com/android/tools/r8/synthesis/CommittedItems.java
index 424fd8d..2681a90 100644
--- a/src/main/java/com/android/tools/r8/synthesis/CommittedItems.java
+++ b/src/main/java/com/android/tools/r8/synthesis/CommittedItems.java
@@ -7,8 +7,6 @@
import com.android.tools.r8.graph.DexClass;
import com.android.tools.r8.graph.DexType;
import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableMap;
-import com.google.common.collect.ImmutableSet;
import java.util.Collection;
import java.util.function.Function;
@@ -28,23 +26,19 @@
// Immutable package accessible fields to allow SyntheticItems creation.
final DexApplication application;
final int nextSyntheticId;
- final ImmutableSet<DexType> legacySyntheticTypes;
- final ImmutableMap<DexType, SyntheticReference> syntheticItems;
- final ImmutableList<DexType> committedTypes;
+ final CommittedSyntheticsCollection committed;
+ final ImmutableList<DexType> committedProgramTypes;
CommittedItems(
int nextSyntheticId,
DexApplication application,
- ImmutableSet<DexType> legacySyntheticTypes,
- ImmutableMap<DexType, SyntheticReference> syntheticItems,
- ImmutableList<DexType> committedTypes) {
- assert verifyTypesAreInApp(application, legacySyntheticTypes);
- assert verifyTypesAreInApp(application, syntheticItems.keySet());
+ CommittedSyntheticsCollection committed,
+ ImmutableList<DexType> committedProgramTypes) {
this.nextSyntheticId = nextSyntheticId;
this.application = application;
- this.legacySyntheticTypes = legacySyntheticTypes;
- this.syntheticItems = syntheticItems;
- this.committedTypes = committedTypes;
+ this.committed = committed;
+ this.committedProgramTypes = committedProgramTypes;
+ committed.verifyTypesAreInApp(application);
}
// Conversion to a mutable synthetic items collection. Should only be used in AppInfo creation.
@@ -56,13 +50,13 @@
return application;
}
- public Collection<DexType> getCommittedTypes() {
- return committedTypes;
+ public Collection<DexType> getCommittedProgramTypes() {
+ return committedProgramTypes;
}
@Deprecated
public Collection<DexType> getLegacySyntheticTypes() {
- return legacySyntheticTypes;
+ return committed.getLegacyTypes();
}
@Override
@@ -70,11 +64,4 @@
// All synthetic types are committed to the application so lookup is just the base lookup.
return baseDefinitionFor.apply(type);
}
-
- private static boolean verifyTypesAreInApp(DexApplication app, Collection<DexType> types) {
- for (DexType type : types) {
- assert app.programDefinitionFor(type) != null : "Missing synthetic: " + type;
- }
- return true;
- }
}
diff --git a/src/main/java/com/android/tools/r8/synthesis/CommittedSyntheticsCollection.java b/src/main/java/com/android/tools/r8/synthesis/CommittedSyntheticsCollection.java
new file mode 100644
index 0000000..053b20b
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/synthesis/CommittedSyntheticsCollection.java
@@ -0,0 +1,246 @@
+// 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.synthesis;
+
+import com.android.tools.r8.graph.DexApplication;
+import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.GraphLens.NonIdentityGraphLens;
+import com.android.tools.r8.graph.PrunedItems;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Sets;
+import java.util.Collection;
+import java.util.Map;
+import java.util.Set;
+import java.util.function.Consumer;
+
+/**
+ * Immutable collection of committed items.
+ *
+ * <p>This structure is to make it easier to pass the items from SyntheticItems to CommittedItems
+ * and back while also providing a builder for updating the committed synthetics.
+ */
+class CommittedSyntheticsCollection {
+
+ static class Builder {
+ private final CommittedSyntheticsCollection parent;
+ private ImmutableMap.Builder<DexType, SyntheticProgramClassReference> newNonLegacyClasses =
+ null;
+ private ImmutableMap.Builder<DexType, SyntheticMethodReference> newNonLegacyMethods = null;
+ private ImmutableSet.Builder<DexType> newLegacyClasses = null;
+
+ public Builder(CommittedSyntheticsCollection parent) {
+ this.parent = parent;
+ }
+
+ public Builder addItem(SyntheticDefinition<?, ?, ?> definition) {
+ if (definition.isProgramDefinition()) {
+ definition.asProgramDefinition().apply(this::addNonLegacyMethod, this::addNonLegacyClass);
+ }
+ return this;
+ }
+
+ public Builder addNonLegacyClass(SyntheticProgramClassDefinition definition) {
+ return addNonLegacyClass(definition.toReference());
+ }
+
+ public Builder addNonLegacyClass(SyntheticProgramClassReference reference) {
+ if (newNonLegacyClasses == null) {
+ newNonLegacyClasses = ImmutableMap.builder();
+ }
+ newNonLegacyClasses.put(reference.getHolder(), reference);
+ return this;
+ }
+
+ public Builder addNonLegacyMethod(SyntheticMethodDefinition definition) {
+ return addNonLegacyMethod(definition.toReference());
+ }
+
+ public Builder addNonLegacyMethod(SyntheticMethodReference reference) {
+ if (newNonLegacyMethods == null) {
+ newNonLegacyMethods = ImmutableMap.builder();
+ }
+ newNonLegacyMethods.put(reference.getHolder(), reference);
+ return this;
+ }
+
+ public Builder addLegacyClasses(Collection<DexProgramClass> classes) {
+ if (newLegacyClasses == null) {
+ newLegacyClasses = ImmutableSet.builder();
+ }
+ classes.forEach(c -> newLegacyClasses.add(c.getType()));
+ return this;
+ }
+
+ public Builder addLegacyClass(DexType type) {
+ if (newLegacyClasses == null) {
+ newLegacyClasses = ImmutableSet.builder();
+ }
+ newLegacyClasses.add(type);
+ return this;
+ }
+
+ public CommittedSyntheticsCollection build() {
+ if (newNonLegacyClasses == null && newNonLegacyMethods == null && newLegacyClasses == null) {
+ return parent;
+ }
+ ImmutableMap<DexType, SyntheticProgramClassReference> allNonLegacyClasses =
+ newNonLegacyClasses == null
+ ? parent.nonLegacyClasses
+ : newNonLegacyClasses.putAll(parent.nonLegacyClasses).build();
+ ImmutableMap<DexType, SyntheticMethodReference> allNonLegacyMethods =
+ newNonLegacyMethods == null
+ ? parent.nonLegacyMethods
+ : newNonLegacyMethods.putAll(parent.nonLegacyMethods).build();
+ ImmutableSet<DexType> allLegacyClasses =
+ newLegacyClasses == null
+ ? parent.legacyTypes
+ : newLegacyClasses.addAll(parent.legacyTypes).build();
+ return new CommittedSyntheticsCollection(
+ allLegacyClasses, allNonLegacyMethods, allNonLegacyClasses);
+ }
+ }
+
+ private static final CommittedSyntheticsCollection EMPTY =
+ new CommittedSyntheticsCollection(ImmutableSet.of(), ImmutableMap.of(), ImmutableMap.of());
+
+ /**
+ * Immutable set of synthetic types in the application (eg, committed).
+ *
+ * <p>TODO(b/158159959): Remove legacy support.
+ */
+ private final ImmutableSet<DexType> legacyTypes;
+
+ /** Mapping from synthetic type to its synthetic method item description. */
+ private final ImmutableMap<DexType, SyntheticMethodReference> nonLegacyMethods;
+
+ /** Mapping from synthetic type to its synthetic class item description. */
+ private final ImmutableMap<DexType, SyntheticProgramClassReference> nonLegacyClasses;
+
+ public CommittedSyntheticsCollection(
+ ImmutableSet<DexType> legacyTypes,
+ ImmutableMap<DexType, SyntheticMethodReference> nonLegacyMethods,
+ ImmutableMap<DexType, SyntheticProgramClassReference> nonLegacyClasses) {
+ this.legacyTypes = legacyTypes;
+ this.nonLegacyMethods = nonLegacyMethods;
+ this.nonLegacyClasses = nonLegacyClasses;
+ assert legacyTypes.size() + nonLegacyMethods.size() + nonLegacyClasses.size()
+ == Sets.union(Sets.union(nonLegacyMethods.keySet(), nonLegacyClasses.keySet()), legacyTypes)
+ .size();
+ }
+
+ public static CommittedSyntheticsCollection empty() {
+ return EMPTY;
+ }
+
+ Builder builder() {
+ return new Builder(this);
+ }
+
+ boolean isEmpty() {
+ return legacyTypes.isEmpty() && nonLegacyMethods.isEmpty() && nonLegacyClasses.isEmpty();
+ }
+
+ boolean containsType(DexType type) {
+ return containsLegacyType(type) || containsNonLegacyType(type);
+ }
+
+ public boolean containsLegacyType(DexType type) {
+ return legacyTypes.contains(type);
+ }
+
+ public boolean containsNonLegacyType(DexType type) {
+ return nonLegacyMethods.containsKey(type) || nonLegacyClasses.containsKey(type);
+ }
+
+ public ImmutableSet<DexType> getLegacyTypes() {
+ return legacyTypes;
+ }
+
+ public ImmutableMap<DexType, SyntheticMethodReference> getNonLegacyMethods() {
+ return nonLegacyMethods;
+ }
+
+ public ImmutableMap<DexType, SyntheticProgramClassReference> getNonLegacyClasses() {
+ return nonLegacyClasses;
+ }
+
+ public SyntheticReference<?, ?, ?> getNonLegacyItem(DexType type) {
+ SyntheticMethodReference reference = nonLegacyMethods.get(type);
+ if (reference != null) {
+ return reference;
+ }
+ return nonLegacyClasses.get(type);
+ }
+
+ public void forEachNonLegacyItem(Consumer<SyntheticReference<?, ?, ?>> fn) {
+ nonLegacyMethods.forEach((t, r) -> fn.accept(r));
+ nonLegacyClasses.forEach((t, r) -> fn.accept(r));
+ }
+
+ CommittedSyntheticsCollection pruneItems(PrunedItems prunedItems) {
+ Set<DexType> removed = prunedItems.getNoLongerSyntheticItems();
+ if (removed.isEmpty()) {
+ return this;
+ }
+ Builder builder = CommittedSyntheticsCollection.empty().builder();
+ boolean changed = false;
+ for (DexType type : legacyTypes) {
+ if (removed.contains(type)) {
+ changed = true;
+ } else {
+ builder.addLegacyClass(type);
+ }
+ }
+ for (SyntheticMethodReference reference : nonLegacyMethods.values()) {
+ if (removed.contains(reference.getHolder())) {
+ changed = true;
+ } else {
+ builder.addNonLegacyMethod(reference);
+ }
+ }
+ for (SyntheticProgramClassReference reference : nonLegacyClasses.values()) {
+ if (removed.contains(reference.getHolder())) {
+ changed = true;
+ } else {
+ builder.addNonLegacyClass(reference);
+ }
+ }
+ return changed ? builder.build() : this;
+ }
+
+ CommittedSyntheticsCollection rewriteWithLens(NonIdentityGraphLens lens) {
+ return new CommittedSyntheticsCollection(
+ lens.rewriteTypes(legacyTypes),
+ rewriteItems(nonLegacyMethods, lens),
+ rewriteItems(nonLegacyClasses, lens));
+ }
+
+ private static <R extends SyntheticReference<R, ?, ?>> ImmutableMap<DexType, R> rewriteItems(
+ Map<DexType, R> items, NonIdentityGraphLens lens) {
+ ImmutableMap.Builder<DexType, R> rewrittenItems = ImmutableMap.builder();
+ for (R reference : items.values()) {
+ R rewritten = reference.rewrite(lens);
+ if (rewritten != null) {
+ rewrittenItems.put(rewritten.getHolder(), rewritten);
+ }
+ }
+ return rewrittenItems.build();
+ }
+
+ boolean verifyTypesAreInApp(DexApplication application) {
+ assert verifyTypesAreInApp(application, legacyTypes);
+ assert verifyTypesAreInApp(application, nonLegacyMethods.keySet());
+ assert verifyTypesAreInApp(application, nonLegacyClasses.keySet());
+ return true;
+ }
+
+ private static boolean verifyTypesAreInApp(DexApplication app, Collection<DexType> types) {
+ for (DexType type : types) {
+ assert app.programDefinitionFor(type) != null : "Missing synthetic: " + type;
+ }
+ return true;
+ }
+}
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 f5d4b66..932359d 100644
--- a/src/main/java/com/android/tools/r8/synthesis/SynthesizingContext.java
+++ b/src/main/java/com/android/tools/r8/synthesis/SynthesizingContext.java
@@ -7,6 +7,7 @@
import static com.android.tools.r8.utils.DescriptorUtils.getDescriptorFromClassBinaryName;
import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexClass;
import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.DexType;
@@ -14,6 +15,7 @@
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.synthesis.SyntheticNaming.Phase;
import java.util.Comparator;
import java.util.Set;
@@ -38,6 +40,13 @@
private final DexType inputContextType;
private final Origin inputContextOrigin;
+ static SynthesizingContext fromNonSyntheticInputContext(DexClass context) {
+ // A context that is itself non-synthetic is the single context, thus both the input context
+ // and synthesizing context coincide.
+ return new SynthesizingContext(
+ context.getContextType(), context.getContextType(), context.getOrigin());
+ }
+
static SynthesizingContext fromNonSyntheticInputContext(ProgramDefinition context) {
// A context that is itself non-synthetic is the single context, thus both the input context
// and synthesizing context coincide.
@@ -53,6 +62,20 @@
return new SynthesizingContext(synthesizingContextType, clazz.type, clazz.origin);
}
+ static SynthesizingContext fromSyntheticContextChange(
+ 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 = factory.createType(descriptor.substring(0, i) + ";");
+ return newContext == oldContext.getSynthesizingContextType()
+ ? oldContext
+ : new SynthesizingContext(newContext, newContext, oldContext.inputContextOrigin);
+ }
+
private SynthesizingContext(
DexType synthesizingContextType, DexType inputContextType, Origin inputContextOrigin) {
this.synthesizingContextType = synthesizingContextType;
@@ -75,14 +98,6 @@
return inputContextOrigin;
}
- DexType createHygienicType(String syntheticId, DexItemFactory factory) {
- // If the context is a synthetic input, then use its annotated context as the hygienic context.
- String contextDesc = synthesizingContextType.toDescriptorString();
- String prefix = contextDesc.substring(0, contextDesc.length() - 1);
- String suffix = SyntheticItems.INTERNAL_SYNTHETIC_CLASS_SEPARATOR + syntheticId + ";";
- return factory.createType(prefix + suffix);
- }
-
SynthesizingContext rewrite(NonIdentityGraphLens lens) {
DexType rewrittenInputeContextType = lens.lookupType(inputContextType);
DexType rewrittenSynthesizingContextType = lens.lookupType(synthesizingContextType);
@@ -127,15 +142,17 @@
void addIfDerivedFromMainDexClass(
DexProgramClass externalSyntheticClass,
MainDexClasses mainDexClasses,
- Set<DexType> allMainDexTypes,
- Set<DexType> derivedMainDexTypesToIgnore) {
+ 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);
- // Mark the type as to be ignored when computing main-dex placement for legacy types.
- derivedMainDexTypesToIgnore.add(inputContextType);
}
}
+
+ @Override
+ public String toString() {
+ return "SynthesizingContext{" + getSynthesizingContextType() + "}";
+ }
}
diff --git a/src/main/java/com/android/tools/r8/synthesis/SyntheticClassBuilder.java b/src/main/java/com/android/tools/r8/synthesis/SyntheticClassBuilder.java
index 4f289c7..01e6c75 100644
--- a/src/main/java/com/android/tools/r8/synthesis/SyntheticClassBuilder.java
+++ b/src/main/java/com/android/tools/r8/synthesis/SyntheticClassBuilder.java
@@ -1,16 +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.synthesis;
import com.android.tools.r8.ProgramResource.Kind;
import com.android.tools.r8.dex.Constants;
import com.android.tools.r8.graph.ClassAccessFlags;
+import com.android.tools.r8.graph.ClassKind;
import com.android.tools.r8.graph.DexAnnotationSet;
+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.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.graph.DexTypeList;
@@ -25,16 +27,20 @@
import java.util.List;
import java.util.function.Consumer;
-public class SyntheticClassBuilder {
+abstract class SyntheticClassBuilder<B extends SyntheticClassBuilder<B, C>, C extends DexClass> {
+
private final DexItemFactory factory;
private final DexType type;
private final Origin origin;
+ private Kind originKind;
private DexType superType;
private DexTypeList interfaces = DexTypeList.empty();
-
- private int nextMethodId = 0;
+ private List<DexEncodedField> staticFields = new ArrayList<>();
+ private List<DexEncodedField> instanceFields = new ArrayList<>();
+ private List<DexEncodedMethod> directMethods = new ArrayList<>();
+ private List<DexEncodedMethod> virtualMethods = new ArrayList<>();
private List<SyntheticMethodBuilder> methods = new ArrayList<>();
SyntheticClassBuilder(DexType type, SynthesizingContext context, DexItemFactory factory) {
@@ -44,6 +50,10 @@
this.superType = factory.objectType;
}
+ public abstract B self();
+
+ public abstract ClassKind<C> getClassKind();
+
public DexItemFactory getFactory() {
return factory;
}
@@ -52,67 +62,92 @@
return type;
}
- private String getNextMethodName() {
- return SyntheticItems.INTERNAL_SYNTHETIC_METHOD_PREFIX + nextMethodId++;
+ public B setInterfaces(List<DexType> interfaces) {
+ this.interfaces =
+ interfaces.isEmpty()
+ ? DexTypeList.empty()
+ : new DexTypeList(interfaces.toArray(DexType.EMPTY_ARRAY));
+ return self();
}
- public SyntheticClassBuilder addMethod(Consumer<SyntheticMethodBuilder> fn) {
- SyntheticMethodBuilder method = new SyntheticMethodBuilder(this, getNextMethodName());
+ public B setOriginKind(Kind originKind) {
+ this.originKind = originKind;
+ return self();
+ }
+
+ public B setStaticFields(List<DexEncodedField> fields) {
+ staticFields.clear();
+ staticFields.addAll(fields);
+ return self();
+ }
+
+ public B setInstanceFields(List<DexEncodedField> fields) {
+ instanceFields.clear();
+ instanceFields.addAll(fields);
+ return self();
+ }
+
+ public B setDirectMethods(Iterable<DexEncodedMethod> methods) {
+ directMethods.clear();
+ methods.forEach(directMethods::add);
+ return self();
+ }
+
+ public B setVirtualMethods(Iterable<DexEncodedMethod> methods) {
+ virtualMethods.clear();
+ methods.forEach(virtualMethods::add);
+ return self();
+ }
+
+ public B addMethod(Consumer<SyntheticMethodBuilder> fn) {
+ SyntheticMethodBuilder method = new SyntheticMethodBuilder(this);
fn.accept(method);
methods.add(method);
- return this;
+ return self();
}
- DexProgramClass build() {
+ public C build() {
ClassAccessFlags accessFlags =
- ClassAccessFlags.fromSharedAccessFlags(Constants.ACC_PUBLIC | Constants.ACC_SYNTHETIC);
- Kind originKind = null;
+ ClassAccessFlags.fromSharedAccessFlags(
+ Constants.ACC_FINAL | Constants.ACC_PUBLIC | Constants.ACC_SYNTHETIC);
DexString sourceFile = null;
NestHostClassAttribute nestHost = null;
List<NestMemberClassAttribute> nestMembers = Collections.emptyList();
EnclosingMethodAttribute enclosingMembers = null;
List<InnerClassAttribute> innerClasses = Collections.emptyList();
- DexEncodedField[] staticFields = DexEncodedField.EMPTY_ARRAY;
- DexEncodedField[] instanceFields = DexEncodedField.EMPTY_ARRAY;
- DexEncodedMethod[] directMethods = DexEncodedMethod.EMPTY_ARRAY;
- DexEncodedMethod[] virtualMethods = DexEncodedMethod.EMPTY_ARRAY;
- assert !methods.isEmpty();
- List<DexEncodedMethod> directs = new ArrayList<>(methods.size());
- List<DexEncodedMethod> virtuals = new ArrayList<>(methods.size());
for (SyntheticMethodBuilder builder : methods) {
DexEncodedMethod method = builder.build();
if (method.isNonPrivateVirtualMethod()) {
- virtuals.add(method);
+ virtualMethods.add(method);
} else {
- directs.add(method);
+ directMethods.add(method);
}
}
- if (!directs.isEmpty()) {
- directMethods = directs.toArray(new DexEncodedMethod[directs.size()]);
- }
- if (!virtuals.isEmpty()) {
- virtualMethods = virtuals.toArray(new DexEncodedMethod[virtuals.size()]);
- }
- long checksum = 7 * (long) directs.hashCode() + 11 * (long) virtuals.hashCode();
- return new DexProgramClass(
- type,
- originKind,
- origin,
- accessFlags,
- superType,
- interfaces,
- sourceFile,
- nestHost,
- nestMembers,
- enclosingMembers,
- innerClasses,
- ClassSignature.noSignature(),
- DexAnnotationSet.empty(),
- staticFields,
- instanceFields,
- directMethods,
- virtualMethods,
- factory.getSkipNameValidationForTesting(),
- c -> checksum);
+ long checksum =
+ 7 * (long) directMethods.hashCode()
+ + 11 * (long) virtualMethods.hashCode()
+ + 13 * (long) staticFields.hashCode()
+ + 17 * (long) instanceFields.hashCode();
+ return getClassKind()
+ .create(
+ type,
+ originKind,
+ origin,
+ accessFlags,
+ superType,
+ interfaces,
+ sourceFile,
+ nestHost,
+ nestMembers,
+ enclosingMembers,
+ innerClasses,
+ ClassSignature.noSignature(),
+ DexAnnotationSet.empty(),
+ staticFields.toArray(DexEncodedField.EMPTY_ARRAY),
+ instanceFields.toArray(DexEncodedField.EMPTY_ARRAY),
+ directMethods.toArray(DexEncodedMethod.EMPTY_ARRAY),
+ virtualMethods.toArray(DexEncodedMethod.EMPTY_ARRAY),
+ factory.getSkipNameValidationForTesting(),
+ c -> checksum);
}
}
diff --git a/src/main/java/com/android/tools/r8/synthesis/SyntheticClassDefinition.java b/src/main/java/com/android/tools/r8/synthesis/SyntheticClassDefinition.java
new file mode 100644
index 0000000..3a915bf
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/synthesis/SyntheticClassDefinition.java
@@ -0,0 +1,47 @@
+// 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.synthesis;
+
+import com.android.tools.r8.graph.DexClass;
+import com.android.tools.r8.synthesis.SyntheticNaming.SyntheticKind;
+
+/**
+ * Definition of a synthetic class item.
+ *
+ * <p>This class is internal to the synthetic items collection, thus package-protected.
+ */
+public abstract class SyntheticClassDefinition<
+ R extends SyntheticClassReference<R, D, C>,
+ D extends SyntheticClassDefinition<R, D, C>,
+ C extends DexClass>
+ extends SyntheticDefinition<R, D, C> {
+
+ final C clazz;
+
+ SyntheticClassDefinition(SyntheticKind kind, SynthesizingContext context, C clazz) {
+ super(kind, context);
+ this.clazz = clazz;
+ }
+
+ @Override
+ public final C getHolder() {
+ return clazz;
+ }
+
+ @Override
+ public boolean isValid() {
+ return clazz.isPublic() && clazz.isFinal() && clazz.accessFlags.isSynthetic();
+ }
+
+ @Override
+ public String toString() {
+ return "SyntheticClass{ clazz = "
+ + clazz.type.toSourceString()
+ + ", kind = "
+ + getKind()
+ + ", context = "
+ + getContext()
+ + " }";
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/synthesis/SyntheticClassReference.java b/src/main/java/com/android/tools/r8/synthesis/SyntheticClassReference.java
new file mode 100644
index 0000000..25142ee
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/synthesis/SyntheticClassReference.java
@@ -0,0 +1,33 @@
+// 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.synthesis;
+
+import com.android.tools.r8.graph.DexClass;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.synthesis.SyntheticNaming.SyntheticKind;
+
+/**
+ * Reference to a synthetic class item.
+ *
+ * <p>This class is internal to the synthetic items collection, thus package-protected.
+ */
+abstract class SyntheticClassReference<
+ R extends SyntheticClassReference<R, D, C>,
+ D extends SyntheticClassDefinition<R, D, C>,
+ C extends DexClass>
+ extends SyntheticReference<R, D, C> {
+
+ final DexType type;
+
+ SyntheticClassReference(SyntheticKind kind, SynthesizingContext context, DexType type) {
+ super(kind, context);
+ this.type = type;
+ }
+
+ @Override
+ DexType getHolder() {
+ return type;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/synthesis/SyntheticClasspathClassBuilder.java b/src/main/java/com/android/tools/r8/synthesis/SyntheticClasspathClassBuilder.java
new file mode 100644
index 0000000..20cd075
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/synthesis/SyntheticClasspathClassBuilder.java
@@ -0,0 +1,30 @@
+// 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.synthesis;
+
+import com.android.tools.r8.ProgramResource.Kind;
+import com.android.tools.r8.graph.ClassKind;
+import com.android.tools.r8.graph.DexClasspathClass;
+import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.graph.DexType;
+
+public class SyntheticClasspathClassBuilder
+ extends SyntheticClassBuilder<SyntheticClasspathClassBuilder, DexClasspathClass> {
+
+ SyntheticClasspathClassBuilder(
+ DexType type, SynthesizingContext context, DexItemFactory factory) {
+ super(type, context, factory);
+ setOriginKind(Kind.CF);
+ }
+
+ @Override
+ public ClassKind<DexClasspathClass> getClassKind() {
+ return ClassKind.CLASSPATH;
+ }
+
+ @Override
+ public SyntheticClasspathClassBuilder self() {
+ return this;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/synthesis/SyntheticClasspathClassDefinition.java b/src/main/java/com/android/tools/r8/synthesis/SyntheticClasspathClassDefinition.java
new file mode 100644
index 0000000..a05d505
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/synthesis/SyntheticClasspathClassDefinition.java
@@ -0,0 +1,66 @@
+// 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.synthesis;
+
+import com.android.tools.r8.graph.DexClasspathClass;
+import com.android.tools.r8.synthesis.SyntheticNaming.SyntheticKind;
+import com.android.tools.r8.utils.structural.RepresentativeMap;
+import com.google.common.hash.Hasher;
+
+/**
+ * Definition of a synthetic classpath class.
+ *
+ * <p>This class is internal to the synthetic items collection, thus package-protected.
+ */
+class SyntheticClasspathClassDefinition
+ extends SyntheticClassDefinition<
+ SyntheticClasspathClassReference, SyntheticClasspathClassDefinition, DexClasspathClass>
+ implements SyntheticClasspathDefinition {
+
+ SyntheticClasspathClassDefinition(
+ SyntheticKind kind, SynthesizingContext context, DexClasspathClass clazz) {
+ super(kind, context, clazz);
+ }
+
+ @Override
+ public boolean isClasspathDefinition() {
+ return true;
+ }
+
+ @Override
+ public SyntheticClasspathDefinition asClasspathDefinition() {
+ return this;
+ }
+
+ @Override
+ SyntheticClasspathClassReference toReference() {
+ return new SyntheticClasspathClassReference(getKind(), getContext(), clazz.getType());
+ }
+
+ @Override
+ public boolean isValid() {
+ return clazz.isPublic() && clazz.isFinal() && clazz.accessFlags.isSynthetic();
+ }
+
+ @Override
+ void internalComputeHash(Hasher hasher, RepresentativeMap map) {
+ clazz.hashWithTypeEquivalence(hasher, map);
+ }
+
+ @Override
+ int internalCompareTo(SyntheticClasspathClassDefinition o, RepresentativeMap map) {
+ return clazz.compareWithTypeEquivalenceTo(o.clazz, map);
+ }
+
+ @Override
+ public String toString() {
+ return "SyntheticClasspathClass{ clazz = "
+ + clazz.type.toSourceString()
+ + ", kind = "
+ + getKind()
+ + ", context = "
+ + getContext()
+ + " }";
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/synthesis/SyntheticClasspathClassReference.java b/src/main/java/com/android/tools/r8/synthesis/SyntheticClasspathClassReference.java
new file mode 100644
index 0000000..5b9ba3c
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/synthesis/SyntheticClasspathClassReference.java
@@ -0,0 +1,42 @@
+// 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.synthesis;
+
+import com.android.tools.r8.graph.DexClass;
+import com.android.tools.r8.graph.DexClasspathClass;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.GraphLens.NonIdentityGraphLens;
+import com.android.tools.r8.synthesis.SyntheticNaming.SyntheticKind;
+import java.util.function.Function;
+
+/**
+ * Reference to a synthetic class item.
+ *
+ * <p>This class is internal to the synthetic items collection, thus package-protected.
+ */
+class SyntheticClasspathClassReference
+ extends SyntheticClassReference<
+ SyntheticClasspathClassReference, SyntheticClasspathClassDefinition, DexClasspathClass> {
+
+ SyntheticClasspathClassReference(SyntheticKind kind, SynthesizingContext context, DexType type) {
+ super(kind, context, type);
+ }
+
+ @Override
+ SyntheticClasspathClassDefinition lookupDefinition(Function<DexType, DexClass> definitions) {
+ DexClass clazz = definitions.apply(type);
+ if (clazz == null) {
+ return null;
+ }
+ assert clazz.isClasspathClass();
+ return new SyntheticClasspathClassDefinition(getKind(), getContext(), clazz.asClasspathClass());
+ }
+
+ @Override
+ SyntheticClasspathClassReference rewrite(NonIdentityGraphLens lens) {
+ assert type == lens.lookupType(type)
+ : "Unexpected classpath rewrite of type " + type.toSourceString();
+ return this;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/synthesis/SyntheticClasspathDefinition.java b/src/main/java/com/android/tools/r8/synthesis/SyntheticClasspathDefinition.java
new file mode 100644
index 0000000..0904dff
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/synthesis/SyntheticClasspathDefinition.java
@@ -0,0 +1,12 @@
+// 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.synthesis;
+
+import com.android.tools.r8.graph.DexClasspathClass;
+
+public interface SyntheticClasspathDefinition {
+
+ DexClasspathClass getHolder();
+}
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 01f8825..071e7b6 100644
--- a/src/main/java/com/android/tools/r8/synthesis/SyntheticDefinition.java
+++ b/src/main/java/com/android/tools/r8/synthesis/SyntheticDefinition.java
@@ -3,31 +3,111 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.synthesis;
-import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.graph.DexClass;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.GraphLens;
+import com.android.tools.r8.synthesis.SyntheticNaming.SyntheticKind;
import com.android.tools.r8.utils.structural.RepresentativeMap;
import com.google.common.hash.HashCode;
+import com.google.common.hash.Hasher;
+import com.google.common.hash.Hashing;
/**
* Base type for the definition of a synthetic item.
*
* <p>This class is internal to the synthetic items collection, thus package-protected.
*/
-abstract class SyntheticDefinition {
+abstract class SyntheticDefinition<
+ R extends SyntheticReference<R, D, C>,
+ D extends SyntheticDefinition<R, D, C>,
+ C extends DexClass> {
+
+ private final SyntheticKind kind;
private final SynthesizingContext context;
- SyntheticDefinition(SynthesizingContext context) {
+ SyntheticDefinition(SyntheticKind kind, SynthesizingContext context) {
+ assert kind != null;
+ assert context != null;
+ this.kind = kind;
this.context = context;
}
- abstract SyntheticReference toReference();
+ public boolean isClasspathDefinition() {
+ return false;
+ }
- SynthesizingContext getContext() {
+ public SyntheticClasspathDefinition asClasspathDefinition() {
+ return null;
+ }
+
+ public boolean isProgramDefinition() {
+ return false;
+ }
+
+ public SyntheticProgramDefinition asProgramDefinition() {
+ return null;
+ }
+
+ abstract R toReference();
+
+ final SyntheticKind getKind() {
+ return kind;
+ }
+
+ final SynthesizingContext getContext() {
return context;
}
- abstract DexProgramClass getHolder();
+ public abstract C getHolder();
- abstract HashCode computeHash(RepresentativeMap map, boolean intermediate);
+ final HashCode computeHash(RepresentativeMap map, boolean intermediate) {
+ Hasher hasher = Hashing.murmur3_128().newHasher();
+ if (intermediate) {
+ // If in intermediate mode, include the context type as sharing is restricted to within a
+ // single context.
+ getContext().getSynthesizingContextType().hashWithTypeEquivalence(hasher, map);
+ }
+ internalComputeHash(hasher, map);
+ return hasher.hash();
+ }
- abstract boolean isEquivalentTo(SyntheticDefinition other, boolean intermediate);
+ abstract void internalComputeHash(Hasher hasher, RepresentativeMap map);
+
+ final boolean isEquivalentTo(D other, boolean includeContext, GraphLens graphLens) {
+ return compareTo(other, includeContext, graphLens) == 0;
+ }
+
+ int compareTo(D other, boolean includeContext, GraphLens graphLens) {
+ if (includeContext) {
+ int order = getContext().compareTo(other.getContext());
+ if (order != 0) {
+ return order;
+ }
+ }
+ DexType thisType = getHolder().getType();
+ DexType otherType = other.getHolder().getType();
+ RepresentativeMap map = null;
+ // If the synthetics have been moved include the original types in the equivalence.
+ if (graphLens.isNonIdentityLens()) {
+ DexType thisOrigType = graphLens.getOriginalType(thisType);
+ DexType otherOrigType = graphLens.getOriginalType(otherType);
+ if (thisType != thisOrigType || otherType != otherOrigType) {
+ map =
+ t -> {
+ if (t == otherType || t == thisOrigType || t == otherOrigType) {
+ return thisType;
+ }
+ return t;
+ };
+ }
+ }
+ if (map == null) {
+ map = t -> t == otherType ? thisType : t;
+ }
+ return internalCompareTo(other, map);
+ }
+
+ abstract int internalCompareTo(D other, RepresentativeMap map);
+
+ public abstract boolean isValid();
}
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 c18b29d..a802761 100644
--- a/src/main/java/com/android/tools/r8/synthesis/SyntheticFinalization.java
+++ b/src/main/java/com/android/tools/r8/synthesis/SyntheticFinalization.java
@@ -6,22 +6,32 @@
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexAnnotation;
import com.android.tools.r8.graph.DexApplication;
+import com.android.tools.r8.graph.DexClass;
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.DexProgramClass;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.GraphLens;
-import com.android.tools.r8.graph.GraphLens.Builder;
import com.android.tools.r8.graph.GraphLens.NestedGraphLens;
+import com.android.tools.r8.graph.GraphLens.NonIdentityGraphLens;
import com.android.tools.r8.graph.PrunedItems;
+import com.android.tools.r8.graph.TreeFixerBase;
+import com.android.tools.r8.ir.code.NumberGenerator;
import com.android.tools.r8.shaking.MainDexClasses;
-import com.android.tools.r8.utils.DescriptorUtils;
+import com.android.tools.r8.synthesis.SyntheticNaming.SyntheticKind;
import com.android.tools.r8.utils.InternalOptions;
+import com.android.tools.r8.utils.SetUtils;
+import com.android.tools.r8.utils.collections.BidirectionalManyToManyRepresentativeMap;
+import com.android.tools.r8.utils.collections.BidirectionalManyToOneRepresentativeHashMap;
+import com.android.tools.r8.utils.collections.BidirectionalManyToOneRepresentativeMap;
+import com.android.tools.r8.utils.collections.BidirectionalOneToOneHashMap;
import com.android.tools.r8.utils.structural.RepresentativeMap;
import com.google.common.collect.ArrayListMultimap;
+import com.google.common.collect.ImmutableCollection;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
-import com.google.common.collect.ImmutableSet;
import com.google.common.collect.ListMultimap;
import com.google.common.collect.Sets;
import com.google.common.hash.HashCode;
@@ -32,37 +42,147 @@
import java.util.IdentityHashMap;
import java.util.List;
import java.util.Map;
+import java.util.Map.Entry;
import java.util.Set;
import java.util.TreeSet;
-import java.util.function.Predicate;
+import java.util.function.BiConsumer;
+import java.util.function.Function;
public class SyntheticFinalization {
public static class Result {
public final CommittedItems commit;
+ public final NonIdentityGraphLens lens;
public final PrunedItems prunedItems;
- public Result(CommittedItems commit, PrunedItems prunedItems) {
+ public Result(
+ CommittedItems commit, SyntheticFinalizationGraphLens lens, PrunedItems prunedItems) {
this.commit = commit;
+ this.lens = lens;
this.prunedItems = prunedItems;
}
}
- private static class EquivalenceGroup<T extends SyntheticDefinition & Comparable<T>>
- implements Comparable<EquivalenceGroup<T>> {
- private List<T> members;
+ public static class SyntheticFinalizationGraphLens extends NestedGraphLens {
- EquivalenceGroup(T singleton) {
- this(singleton, Collections.singletonList(singleton));
+ private final Map<DexType, DexType> syntheticTypeMap;
+ private final Map<DexMethod, DexMethod> syntheticMethodsMap;
+
+ private SyntheticFinalizationGraphLens(
+ GraphLens previous,
+ Map<DexType, DexType> syntheticClassesMap,
+ Map<DexMethod, DexMethod> syntheticMethodsMap,
+ Map<DexType, DexType> typeMap,
+ BidirectionalManyToOneRepresentativeMap<DexField, DexField> fieldMap,
+ Map<DexMethod, DexMethod> methodMap,
+ BidirectionalManyToManyRepresentativeMap<DexMethod, DexMethod> originalMethodSignatures,
+ DexItemFactory factory) {
+ super(typeMap, methodMap, fieldMap, originalMethodSignatures, previous, factory);
+ this.syntheticTypeMap = syntheticClassesMap;
+ this.syntheticMethodsMap = syntheticMethodsMap;
}
- EquivalenceGroup(T representative, List<T> members) {
+ // 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
+
+ @Override
+ public DexField getRenamedFieldSignature(DexField originalField) {
+ if (syntheticTypeMap.containsKey(originalField.holder)) {
+ DexField renamed = fieldMap.get(originalField);
+ if (renamed != null) {
+ return renamed;
+ }
+ }
+ return super.getRenamedFieldSignature(originalField);
+ }
+
+ @Override
+ public DexMethod getRenamedMethodSignature(DexMethod originalMethod, GraphLens applied) {
+ if (syntheticTypeMap.containsKey(originalMethod.holder)) {
+ DexMethod renamed = methodMap.get(originalMethod);
+ if (renamed != null) {
+ return renamed;
+ }
+ }
+ DexMethod renamed = syntheticMethodsMap.get(originalMethod);
+ return renamed != null ? renamed : super.getRenamedMethodSignature(originalMethod, applied);
+ }
+ }
+
+ private static class Builder {
+
+ // Forward mapping of internal to external synthetics.
+ Map<DexType, DexType> syntheticClassesMap = new IdentityHashMap<>();
+ Map<DexMethod, DexMethod> syntheticMethodsMap = new IdentityHashMap<>();
+
+ Map<DexType, DexType> typeMap = new IdentityHashMap<>();
+ BidirectionalManyToOneRepresentativeHashMap<DexField, DexField> fieldMap =
+ new BidirectionalManyToOneRepresentativeHashMap<>();
+ Map<DexMethod, DexMethod> methodMap = new IdentityHashMap<>();
+
+ protected final BidirectionalOneToOneHashMap<DexMethod, DexMethod> originalMethodSignatures =
+ new BidirectionalOneToOneHashMap<>();
+
+ void moveSyntheticClass(DexType from, DexType to) {
+ assert !syntheticClassesMap.containsKey(from);
+ syntheticClassesMap.put(from, to);
+ typeMap.put(from, to);
+ }
+
+ void moveSyntheticMethod(DexMethod from, DexMethod to) {
+ assert !syntheticMethodsMap.containsKey(from);
+ syntheticMethodsMap.put(from, to);
+ methodMap.put(from, to);
+ }
+
+ void move(DexType from, DexType to) {
+ typeMap.put(from, to);
+ }
+
+ void move(DexField from, DexField to) {
+ fieldMap.put(from, to);
+ }
+
+ void move(DexMethod from, DexMethod to) {
+ methodMap.put(from, to);
+ originalMethodSignatures.put(to, from);
+ }
+
+ SyntheticFinalizationGraphLens build(GraphLens previous, DexItemFactory factory) {
+ assert verifySubMap(syntheticClassesMap, typeMap);
+ if (typeMap.isEmpty() && fieldMap.isEmpty() && methodMap.isEmpty()) {
+ return null;
+ }
+ return new SyntheticFinalizationGraphLens(
+ previous,
+ syntheticClassesMap,
+ syntheticMethodsMap,
+ typeMap,
+ fieldMap,
+ methodMap,
+ originalMethodSignatures,
+ factory);
+ }
+
+ private static <K, V> boolean verifySubMap(Map<K, V> sub, Map<K, V> sup) {
+ for (Entry<K, V> entry : sub.entrySet()) {
+ assert sup.get(entry.getKey()) == entry.getValue();
+ }
+ return true;
+ }
+ }
+
+ public static class EquivalenceGroup<T extends SyntheticDefinition<?, T, ?>> {
+ private final List<T> members;
+
+ public EquivalenceGroup(T representative, List<T> members) {
assert !members.isEmpty();
assert members.get(0) == representative;
this.members = members;
}
- T getRepresentative() {
+ public T getRepresentative() {
return members.get(0);
}
@@ -70,89 +190,115 @@
return members;
}
+ public int compareToIncludingContext(EquivalenceGroup<T> other, GraphLens graphLens) {
+ return getRepresentative().compareTo(other.getRepresentative(), true, graphLens);
+ }
+
@Override
- public int compareTo(EquivalenceGroup<T> other) {
- return getRepresentative().compareTo(other.getRepresentative());
+ public String toString() {
+ return "EquivalenceGroup{ members = "
+ + members.size()
+ + ", repr = "
+ + getRepresentative()
+ + " }";
}
}
private final InternalOptions options;
- private final ImmutableSet<DexType> legacySyntheticTypes;
- private final ImmutableMap<DexType, SyntheticReference> syntheticItems;
+ private final CommittedSyntheticsCollection synthetics;
- SyntheticFinalization(
- InternalOptions options,
- ImmutableSet<DexType> legacySyntheticTypes,
- ImmutableMap<DexType, SyntheticReference> syntheticItems) {
+ SyntheticFinalization(InternalOptions options, CommittedSyntheticsCollection synthetics) {
this.options = options;
- this.legacySyntheticTypes = legacySyntheticTypes;
- this.syntheticItems = syntheticItems;
+ this.synthetics = synthetics;
}
public Result computeFinalSynthetics(AppView<?> appView) {
assert verifyNoNestedSynthetics();
- DexApplication application = appView.appInfo().app();
+ DexApplication application;
MainDexClasses mainDexClasses = appView.appInfo().getMainDexClasses();
- GraphLens graphLens = appView.graphLens();
-
- Map<DexType, SyntheticMethodDefinition> methodDefinitions =
- lookupSyntheticMethodDefinitions(application);
-
- Collection<List<SyntheticMethodDefinition>> potentialEquivalences =
- computePotentialEquivalences(methodDefinitions, options.intermediate);
-
- Map<DexType, EquivalenceGroup<SyntheticMethodDefinition>> equivalences =
- computeActualEquivalences(potentialEquivalences, options.intermediate, options.itemFactory);
-
- Builder lensBuilder = NestedGraphLens.builder();
- List<DexProgramClass> newProgramClasses = new ArrayList<>();
- List<DexProgramClass> finalSyntheticClasses = new ArrayList<>();
- Set<DexType> derivedMainDexTypesToIgnore = Sets.newIdentityHashSet();
- buildLensAndProgram(
- appView,
- equivalences,
- syntheticItems::containsKey,
- mainDexClasses,
- lensBuilder,
- newProgramClasses,
- finalSyntheticClasses,
- derivedMainDexTypesToIgnore);
-
- newProgramClasses.addAll(finalSyntheticClasses);
+ Builder lensBuilder = new Builder();
+ ImmutableMap.Builder<DexType, SyntheticMethodReference> finalMethodsBuilder =
+ ImmutableMap.builder();
+ ImmutableMap.Builder<DexType, SyntheticProgramClassReference> finalClassesBuilder =
+ ImmutableMap.builder();
+ List<DexProgramClass> finalSyntheticProgramDefinitions = new ArrayList<>();
+ {
+ Map<DexType, NumberGenerator> generators = new IdentityHashMap<>();
+ application =
+ buildLensAndProgram(
+ appView,
+ computeEquivalences(appView, synthetics.getNonLegacyMethods().values(), generators),
+ computeEquivalences(appView, synthetics.getNonLegacyClasses().values(), generators),
+ mainDexClasses,
+ lensBuilder,
+ (clazz, reference) -> {
+ finalSyntheticProgramDefinitions.add(clazz);
+ finalClassesBuilder.put(clazz.getType(), reference);
+ },
+ (clazz, reference) -> {
+ finalSyntheticProgramDefinitions.add(clazz);
+ finalMethodsBuilder.put(clazz.getType(), reference);
+ });
+ }
+ ImmutableMap<DexType, SyntheticMethodReference> finalMethods = finalMethodsBuilder.build();
+ ImmutableMap<DexType, SyntheticProgramClassReference> finalClasses =
+ finalClassesBuilder.build();
handleSynthesizedClassMapping(
- finalSyntheticClasses, application, options, mainDexClasses, derivedMainDexTypesToIgnore);
+ finalSyntheticProgramDefinitions,
+ application,
+ options,
+ mainDexClasses,
+ lensBuilder.typeMap);
- DexApplication app = application.builder().replaceProgramClasses(newProgramClasses).build();
-
- appView.setGraphLens(lensBuilder.build(options.itemFactory, graphLens));
assert appView.appInfo().getMainDexClasses() == mainDexClasses;
- Set<DexType> finalSyntheticTypes = Sets.newIdentityHashSet();
- finalSyntheticClasses.forEach(clazz -> finalSyntheticTypes.add(clazz.getType()));
-
Set<DexType> prunedSynthetics = Sets.newIdentityHashSet();
- for (DexType type : syntheticItems.keySet()) {
- if (!finalSyntheticTypes.contains(type)) {
- prunedSynthetics.add(type);
- }
- }
+ synthetics.forEachNonLegacyItem(
+ reference -> {
+ DexType type = reference.getHolder();
+ if (!finalMethods.containsKey(type) && !finalClasses.containsKey(type)) {
+ prunedSynthetics.add(type);
+ }
+ });
return new Result(
new CommittedItems(
SyntheticItems.INVALID_ID_AFTER_SYNTHETIC_FINALIZATION,
- app,
- legacySyntheticTypes,
- ImmutableMap.of(),
+ application,
+ new CommittedSyntheticsCollection(
+ synthetics.getLegacyTypes(), finalMethods, finalClasses),
ImmutableList.of()),
- PrunedItems.builder().setPrunedApp(app).addRemovedClasses(prunedSynthetics).build());
+ lensBuilder.build(appView.graphLens(), appView.dexItemFactory()),
+ PrunedItems.builder()
+ .setPrunedApp(application)
+ .addRemovedClasses(prunedSynthetics)
+ .build());
+ }
+
+ private <R extends SyntheticReference<R, D, ?>, D extends SyntheticDefinition<R, D, ?>>
+ Map<DexType, EquivalenceGroup<D>> computeEquivalences(
+ AppView<?> appView,
+ ImmutableCollection<R> references,
+ Map<DexType, NumberGenerator> generators) {
+ boolean intermediate = appView.options().intermediate;
+ Map<DexType, D> definitions = lookupDefinitions(appView, references);
+ Collection<List<D>> potentialEquivalences =
+ computePotentialEquivalences(
+ definitions, intermediate, appView.dexItemFactory(), appView.graphLens());
+ return computeActualEquivalences(potentialEquivalences, generators, appView, intermediate);
+ }
+
+ private boolean isNotSyntheticType(DexType type) {
+ return !synthetics.containsNonLegacyType(type);
}
private boolean verifyNoNestedSynthetics() {
// Check that a context is never itself synthetic class.
- for (SyntheticReference item : syntheticItems.values()) {
- assert !syntheticItems.containsKey(item.getContext().getSynthesizingContextType());
- }
+ synthetics.forEachNonLegacyItem(
+ item -> {
+ assert isNotSyntheticType(item.getContext().getSynthesizingContextType());
+ });
return true;
}
@@ -161,7 +307,7 @@
DexApplication application,
InternalOptions options,
MainDexClasses mainDexClasses,
- Set<DexType> derivedMainDexTypesToIgnore) {
+ Map<DexType, DexType> derivedMainDexTypesToIgnore) {
boolean includeSynthesizedClassMappingInOutput = shouldAnnotateSynthetics(options);
if (includeSynthesizedClassMappingInOutput) {
updateSynthesizedClassMapping(application, finalSyntheticClasses);
@@ -177,7 +323,7 @@
DexApplication application, List<DexProgramClass> finalSyntheticClasses) {
ListMultimap<DexProgramClass, DexProgramClass> originalToSynthesized =
ArrayListMultimap.create();
- for (DexType type : legacySyntheticTypes) {
+ for (DexType type : synthetics.getLegacyTypes()) {
DexProgramClass clazz = DexProgramClass.asProgramClassOrNull(application.definitionFor(type));
if (clazz != null) {
for (DexProgramClass origin : clazz.getSynthesizedFrom()) {
@@ -214,7 +360,7 @@
private void updateMainDexListWithSynthesizedClassMap(
DexApplication application,
MainDexClasses mainDexClasses,
- Set<DexType> derivedMainDexTypesToIgnore) {
+ Map<DexType, DexType> derivedMainDexTypesToIgnore) {
if (mainDexClasses.isEmpty()) {
return;
}
@@ -228,12 +374,11 @@
DexAnnotation.readAnnotationSynthesizedClassMap(
programClass, application.dexItemFactory);
for (DexType type : derived) {
- if (!derivedMainDexTypesToIgnore.contains(type)) {
- DexProgramClass syntheticClass =
- DexProgramClass.asProgramClassOrNull(application.definitionFor(type));
- if (syntheticClass != null) {
- newMainDexClasses.add(syntheticClass);
- }
+ DexType mappedType = derivedMainDexTypesToIgnore.getOrDefault(type, type);
+ DexProgramClass syntheticClass =
+ DexProgramClass.asProgramClassOrNull(application.definitionFor(mappedType));
+ if (syntheticClass != null) {
+ newMainDexClasses.add(syntheticClass);
}
}
}
@@ -248,23 +393,17 @@
}
}
- private static void buildLensAndProgram(
+ private static DexApplication buildLensAndProgram(
AppView<?> appView,
Map<DexType, EquivalenceGroup<SyntheticMethodDefinition>> syntheticMethodGroups,
- Predicate<DexType> isSyntheticType,
+ Map<DexType, EquivalenceGroup<SyntheticProgramClassDefinition>> syntheticClassGroups,
MainDexClasses mainDexClasses,
Builder lensBuilder,
- List<DexProgramClass> normalClasses,
- List<DexProgramClass> newSyntheticClasses,
- Set<DexType> derivedMainDexTypesToIgnore) {
+ BiConsumer<DexProgramClass, SyntheticProgramClassReference> addFinalSyntheticClass,
+ BiConsumer<DexProgramClass, SyntheticMethodReference> addFinalSyntheticMethod) {
+ DexApplication application = appView.appInfo().app();
DexItemFactory factory = appView.dexItemFactory();
-
- for (DexProgramClass clazz : appView.appInfo().classes()) {
- if (!isSyntheticType.test(clazz.type)) {
- assert SyntheticItems.verifyNotInternalSynthetic(clazz.type);
- normalClasses.add(clazz);
- }
- }
+ List<DexProgramClass> newProgramClasses = new ArrayList<>();
// TODO(b/168584485): Remove this once class-mapping support is removed.
Set<DexType> derivedMainDexTypes = Sets.newIdentityHashSet();
@@ -280,60 +419,177 @@
}
});
+ Set<DexType> pruned = Sets.newIdentityHashSet();
syntheticMethodGroups.forEach(
(syntheticType, syntheticGroup) -> {
SyntheticMethodDefinition representative = syntheticGroup.getRepresentative();
SynthesizingContext context = representative.getContext();
context.registerPrefixRewriting(syntheticType, appView);
- SyntheticClassBuilder builder =
- new SyntheticClassBuilder(syntheticType, context, factory);
- // TODO(b/158159959): Support grouping multiple methods per synthetic class.
- builder.addMethod(
- methodBuilder -> {
- DexEncodedMethod definition = representative.getMethod().getDefinition();
- methodBuilder
- .setAccessFlags(definition.accessFlags)
- .setProto(definition.getProto())
- .setClassFileVersion(
- definition.hasClassFileVersion() ? definition.getClassFileVersion() : null)
- .setCode(m -> definition.getCode());
- });
- DexProgramClass externalSyntheticClass = builder.build();
- if (shouldAnnotateSynthetics(appView.options())) {
- externalSyntheticClass.setAnnotations(
- externalSyntheticClass
- .annotations()
- .getWithAddedOrReplaced(
- DexAnnotation.createAnnotationSynthesizedClass(
- context.getSynthesizingContextType(), factory)));
- }
+ DexProgramClass externalSyntheticClass =
+ createExternalMethodClass(syntheticType, representative, factory);
+ newProgramClasses.add(externalSyntheticClass);
+ addSyntheticMarker(representative.getKind(), externalSyntheticClass, context, appView);
assert externalSyntheticClass.getMethodCollection().size() == 1;
DexEncodedMethod externalSyntheticMethod =
externalSyntheticClass.methods().iterator().next();
- newSyntheticClasses.add(externalSyntheticClass);
for (SyntheticMethodDefinition member : syntheticGroup.getMembers()) {
- if (member.getMethod().getReference() != externalSyntheticMethod.method) {
- lensBuilder.map(member.getMethod().getReference(), externalSyntheticMethod.method);
- }
- member
- .getContext()
- .addIfDerivedFromMainDexClass(
- externalSyntheticClass,
- mainDexClasses,
- derivedMainDexTypes,
- derivedMainDexTypesToIgnore);
- // TODO(b/168584485): Remove this once class-mapping support is removed.
- DexProgramClass from =
- DexProgramClass.asProgramClassOrNull(
- appView
- .appInfo()
- .definitionForWithoutExistenceAssert(
- member.getContext().getSynthesizingContextType()));
- if (from != null) {
- externalSyntheticClass.addSynthesizedFrom(from);
+ DexMethod memberReference = member.getMethod().getReference();
+ pruned.add(member.getHolder().getType());
+ if (memberReference != externalSyntheticMethod.method) {
+ lensBuilder.moveSyntheticMethod(memberReference, externalSyntheticMethod.method);
}
}
});
+
+ List<DexProgramClass> deduplicatedClasses = new ArrayList<>();
+ syntheticClassGroups.forEach(
+ (syntheticType, syntheticGroup) -> {
+ SyntheticProgramClassDefinition representative = syntheticGroup.getRepresentative();
+ SynthesizingContext context = representative.getContext();
+ context.registerPrefixRewriting(syntheticType, appView);
+ DexProgramClass externalSyntheticClass = representative.getHolder();
+ newProgramClasses.add(externalSyntheticClass);
+ addSyntheticMarker(representative.getKind(), externalSyntheticClass, context, appView);
+ for (SyntheticProgramClassDefinition member : syntheticGroup.getMembers()) {
+ DexProgramClass memberClass = member.getHolder();
+ DexType memberType = memberClass.getType();
+ pruned.add(memberType);
+ if (memberType != syntheticType) {
+ lensBuilder.moveSyntheticClass(memberType, syntheticType);
+ }
+ // The aliasing of the non-representative members needs to be recorded manually.
+ if (member != representative) {
+ deduplicatedClasses.add(memberClass);
+ }
+ }
+ });
+
+ for (DexProgramClass clazz : application.classes()) {
+ if (!pruned.contains(clazz.type)) {
+ newProgramClasses.add(clazz);
+ }
+ }
+ application = application.builder().replaceProgramClasses(newProgramClasses).build();
+
+ // We can only assert that the method container classes are in here as the classes need
+ // to be rewritten by the tree-fixer.
+ for (DexType key : syntheticMethodGroups.keySet()) {
+ assert application.definitionFor(key) != null;
+ }
+
+ DexApplication.Builder<?> builder = application.builder();
+ TreeFixerBase treeFixer =
+ new TreeFixerBase(appView) {
+ @Override
+ public DexType mapClassType(DexType type) {
+ return lensBuilder.syntheticClassesMap.getOrDefault(type, type);
+ }
+
+ @Override
+ public void recordFieldChange(DexField from, DexField to) {
+ lensBuilder.move(from, to);
+ }
+
+ @Override
+ public void recordMethodChange(DexMethod from, DexMethod to) {
+ lensBuilder.move(from, to);
+ }
+
+ @Override
+ public void recordClassChange(DexType from, DexType to) {
+ lensBuilder.move(from, to);
+ }
+ };
+ treeFixer.fixupClasses(deduplicatedClasses);
+ builder.replaceProgramClasses(treeFixer.fixupClasses(application.classes()));
+ application = builder.build();
+
+ // Add the synthesized from after repackaging which changed class definitions.
+ final DexApplication appForLookup = application;
+ syntheticClassGroups.forEach(
+ (syntheticType, syntheticGroup) -> {
+ DexProgramClass externalSyntheticClass = appForLookup.programDefinitionFor(syntheticType);
+ addFinalSyntheticClass.accept(
+ externalSyntheticClass, syntheticGroup.getRepresentative().toReference());
+ for (SyntheticProgramClassDefinition member : syntheticGroup.getMembers()) {
+ addMainDexAndSynthesizedFromForMember(
+ member,
+ externalSyntheticClass,
+ mainDexClasses,
+ derivedMainDexTypes,
+ appForLookup::programDefinitionFor);
+ }
+ });
+ syntheticMethodGroups.forEach(
+ (syntheticType, syntheticGroup) -> {
+ DexProgramClass externalSyntheticClass = appForLookup.programDefinitionFor(syntheticType);
+ addFinalSyntheticMethod.accept(
+ externalSyntheticClass, syntheticGroup.getRepresentative().toReference());
+ for (SyntheticMethodDefinition member : syntheticGroup.getMembers()) {
+ addMainDexAndSynthesizedFromForMember(
+ member,
+ externalSyntheticClass,
+ mainDexClasses,
+ derivedMainDexTypes,
+ appForLookup::programDefinitionFor);
+ }
+ });
+
+ for (DexType key : syntheticMethodGroups.keySet()) {
+ assert application.definitionFor(key) != null;
+ }
+
+ for (DexType key : syntheticClassGroups.keySet()) {
+ assert application.definitionFor(key) != null;
+ }
+
+ return application;
+ }
+
+ private static void addSyntheticMarker(
+ SyntheticKind kind,
+ DexProgramClass externalSyntheticClass,
+ SynthesizingContext context,
+ AppView<?> appView) {
+ if (shouldAnnotateSynthetics(appView.options())) {
+ SyntheticMarker.addMarkerToClass(
+ externalSyntheticClass, kind, context, appView.dexItemFactory());
+ }
+ }
+
+ private static DexProgramClass createExternalMethodClass(
+ DexType syntheticType, SyntheticMethodDefinition representative, DexItemFactory factory) {
+ SyntheticProgramClassBuilder builder =
+ new SyntheticProgramClassBuilder(syntheticType, representative.getContext(), factory);
+ // TODO(b/158159959): Support grouping multiple methods per synthetic class.
+ builder.addMethod(
+ methodBuilder -> {
+ DexEncodedMethod definition = representative.getMethod().getDefinition();
+ methodBuilder
+ .setName(SyntheticNaming.INTERNAL_SYNTHETIC_METHOD_PREFIX)
+ .setAccessFlags(definition.accessFlags)
+ .setProto(definition.getProto())
+ .setClassFileVersion(
+ definition.hasClassFileVersion() ? definition.getClassFileVersion() : null)
+ .setCode(m -> definition.getCode());
+ });
+ return builder.build();
+ }
+
+ private static void addMainDexAndSynthesizedFromForMember(
+ SyntheticDefinition<?, ?, ?> member,
+ DexProgramClass externalSyntheticClass,
+ MainDexClasses mainDexClasses,
+ Set<DexType> derivedMainDexTypes,
+ Function<DexType, DexProgramClass> definitions) {
+ member
+ .getContext()
+ .addIfDerivedFromMainDexClass(externalSyntheticClass, mainDexClasses, derivedMainDexTypes);
+ // TODO(b/168584485): Remove this once class-mapping support is removed.
+ DexProgramClass from = definitions.apply(member.getContext().getSynthesizingContextType());
+ if (from != null) {
+ externalSyntheticClass.addSynthesizedFrom(from);
+ }
}
private static boolean shouldAnnotateSynthetics(InternalOptions options) {
@@ -345,15 +601,18 @@
return options.intermediate && !options.cfToCfDesugar;
}
- private static <T extends SyntheticDefinition & Comparable<T>>
+ private <T extends SyntheticDefinition<?, T, ?>>
Map<DexType, EquivalenceGroup<T>> computeActualEquivalences(
- Collection<List<T>> potentialEquivalences, boolean intermediate, DexItemFactory factory) {
+ Collection<List<T>> potentialEquivalences,
+ Map<DexType, NumberGenerator> generators,
+ AppView<?> appView,
+ boolean intermediate) {
Map<DexType, List<EquivalenceGroup<T>>> groupsPerContext = new IdentityHashMap<>();
potentialEquivalences.forEach(
members -> {
- List<List<T>> groups = groupEquivalent(members, intermediate);
+ List<List<T>> groups = groupEquivalent(members, intermediate, appView.graphLens());
for (List<T> group : groups) {
- T representative = findDeterministicRepresentative(group);
+ T representative = findDeterministicRepresentative(group, appView.graphLens());
// The representative is required to be the first element of the group.
group.remove(representative);
group.add(0, representative);
@@ -368,27 +627,31 @@
Map<DexType, EquivalenceGroup<T>> equivalences = new IdentityHashMap<>();
groupsPerContext.forEach(
(context, groups) -> {
- groups.sort(EquivalenceGroup::compareTo);
+ // Sort the equivalence groups that go into 'context' including the context type of the
+ // representative which is equal to 'context' here (see assert below).
+ groups.sort((a, b) -> a.compareToIncludingContext(b, appView.graphLens()));
for (int i = 0; i < groups.size(); i++) {
EquivalenceGroup<T> group = groups.get(i);
+ assert group.getRepresentative().getContext().getSynthesizingContextType() == context;
// Two equivalence groups in same context type must be distinct otherwise the assignment
// of the synthetic name will be non-deterministic between the two.
- assert i == 0 || checkGroupsAreDistinct(groups.get(i - 1), group);
- DexType representativeType = createExternalType(context, i, factory);
+ assert i == 0 || checkGroupsAreDistinct(groups.get(i - 1), group, appView.graphLens());
+ SyntheticKind kind = group.members.get(0).getKind();
+ DexType representativeType = createExternalType(kind, context, generators, appView);
equivalences.put(representativeType, group);
}
});
return equivalences;
}
- private static <T extends SyntheticDefinition & Comparable<T>> List<List<T>> groupEquivalent(
- List<T> potentialEquivalence, boolean intermediate) {
+ private static <T extends SyntheticDefinition<?, T, ?>> List<List<T>> groupEquivalent(
+ List<T> potentialEquivalence, boolean intermediate, GraphLens graphLens) {
List<List<T>> groups = new ArrayList<>();
// Each other member is in a shared group if it is actually equivalent to the first member.
for (T synthetic : potentialEquivalence) {
boolean requireNewGroup = true;
for (List<T> group : groups) {
- if (synthetic.isEquivalentTo(group.get(0), intermediate)) {
+ if (synthetic.isEquivalentTo(group.get(0), intermediate, graphLens)) {
requireNewGroup = false;
group.add(synthetic);
break;
@@ -403,42 +666,78 @@
return groups;
}
- private static <T extends SyntheticDefinition & Comparable<T>> boolean checkGroupsAreDistinct(
- EquivalenceGroup<T> g1, EquivalenceGroup<T> g2) {
- assert g1.compareTo(g2) != 0;
+ private static <T extends SyntheticDefinition<?, T, ?>> boolean checkGroupsAreDistinct(
+ EquivalenceGroup<T> g1, EquivalenceGroup<T> g2, GraphLens graphLens) {
+ int order = g1.compareToIncludingContext(g2, graphLens);
+ assert order != 0;
+ assert order != g2.compareToIncludingContext(g1, graphLens);
return true;
}
- private static <T extends SyntheticDefinition & Comparable<T>> T findDeterministicRepresentative(
- List<T> members) {
+ private static <T extends SyntheticDefinition<?, T, ?>> T findDeterministicRepresentative(
+ List<T> members, GraphLens graphLens) {
// Pick a deterministic member as representative.
T smallest = members.get(0);
for (int i = 1; i < members.size(); i++) {
T next = members.get(i);
- if (next.compareTo(smallest) < 0) {
+ if (next.compareTo(smallest, true, graphLens) < 0) {
smallest = next;
}
}
return smallest;
}
- private static DexType createExternalType(
- DexType representativeContext, int nextContextId, DexItemFactory factory) {
- return factory.createType(
- DescriptorUtils.getDescriptorFromClassBinaryName(
- representativeContext.getInternalName()
- + SyntheticItems.EXTERNAL_SYNTHETIC_CLASS_SEPARATOR
- + nextContextId));
+ private DexType createExternalType(
+ SyntheticKind kind,
+ DexType representativeContext,
+ Map<DexType, NumberGenerator> generators,
+ AppView<?> appView) {
+ NumberGenerator generator =
+ generators.computeIfAbsent(representativeContext, k -> new NumberGenerator());
+ DexType externalType;
+ do {
+ externalType =
+ SyntheticNaming.createExternalType(
+ kind,
+ representativeContext,
+ Integer.toString(generator.next()),
+ appView.dexItemFactory());
+ DexClass clazz = appView.appInfo().definitionForWithoutExistenceAssert(externalType);
+ if (clazz != null && isNotSyntheticType(clazz.type)) {
+ assert options.testing.allowConflictingSyntheticTypes
+ : "Unexpected creation of an existing external synthetic type: " + clazz;
+ externalType = null;
+ }
+ } while (externalType == null);
+ return externalType;
}
- private static <T extends SyntheticDefinition> Collection<List<T>> computePotentialEquivalences(
- Map<DexType, T> definitions, boolean intermediate) {
+ private static <T extends SyntheticDefinition<?, T, ?>>
+ Collection<List<T>> computePotentialEquivalences(
+ Map<DexType, T> definitions,
+ boolean intermediate,
+ DexItemFactory factory,
+ GraphLens graphLens) {
if (definitions.isEmpty()) {
return Collections.emptyList();
}
- Set<DexType> allTypes = definitions.keySet();
- DexType representative = allTypes.iterator().next();
- RepresentativeMap map = t -> allTypes.contains(t) ? representative : t;
+ // Map all synthetic types to the java 'void' type. This is not an actual valid type, so it
+ // cannot collide with any valid java type providing a good hashing key for the synthetics.
+ Set<DexType> syntheticTypes;
+ if (graphLens.isIdentityLens()) {
+ syntheticTypes = definitions.keySet();
+ } else {
+ // If the synthetics are renamed include their original names in the equivalence too.
+ syntheticTypes = SetUtils.newIdentityHashSet(definitions.size() * 2);
+ definitions
+ .keySet()
+ .forEach(
+ t -> {
+ syntheticTypes.add(t);
+ syntheticTypes.add(graphLens.getOriginalType(t));
+ });
+ }
+ RepresentativeMap map = t -> syntheticTypes.contains(t) ? factory.voidType : t;
Map<HashCode, List<T>> equivalences = new HashMap<>(definitions.size());
for (T definition : definitions.values()) {
HashCode hash = definition.computeHash(map, intermediate);
@@ -447,25 +746,24 @@
return equivalences.values();
}
- private Map<DexType, SyntheticMethodDefinition> lookupSyntheticMethodDefinitions(
- DexApplication finalApp) {
- Map<DexType, SyntheticMethodDefinition> methods = new IdentityHashMap<>(syntheticItems.size());
- for (SyntheticReference reference : syntheticItems.values()) {
- SyntheticDefinition definition = reference.lookupDefinition(finalApp::definitionFor);
- if (definition == null || !(definition instanceof SyntheticMethodDefinition)) {
+ private <R extends SyntheticReference<R, D, ?>, D extends SyntheticDefinition<R, D, ?>>
+ Map<DexType, D> lookupDefinitions(AppView<?> appView, Collection<R> references) {
+ Map<DexType, D> definitions = new IdentityHashMap<>(references.size());
+ for (R reference : references) {
+ D definition = reference.lookupDefinition(appView::definitionFor);
+ if (definition == null) {
// We expect pruned definitions to have been removed.
assert false;
continue;
}
- SyntheticMethodDefinition method = (SyntheticMethodDefinition) definition;
- if (SyntheticMethodBuilder.isValidSyntheticMethod(method.getMethod().getDefinition())) {
- methods.put(method.getHolder().getType(), method);
+ if (definition.isValid()) {
+ definitions.put(reference.getHolder(), definition);
} else {
// Failing this check indicates that an optimization has modified the synthetic in a
// disruptive way.
assert false;
}
}
- return methods;
+ return definitions;
}
}
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 bc7d42e..657db43 100644
--- a/src/main/java/com/android/tools/r8/synthesis/SyntheticItems.java
+++ b/src/main/java/com/android/tools/r8/synthesis/SyntheticItems.java
@@ -6,12 +6,9 @@
import com.android.tools.r8.errors.InternalCompilerError;
import com.android.tools.r8.graph.AppInfo;
import com.android.tools.r8.graph.AppView;
-import com.android.tools.r8.graph.ClassAccessFlags;
-import com.android.tools.r8.graph.DexAnnotation;
-import com.android.tools.r8.graph.DexAnnotationSet;
import com.android.tools.r8.graph.DexApplication;
import com.android.tools.r8.graph.DexClass;
-import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.graph.DexClasspathClass;
import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.DexType;
@@ -21,11 +18,8 @@
import com.android.tools.r8.graph.PrunedItems;
import com.android.tools.r8.ir.conversion.MethodProcessingId;
import com.android.tools.r8.synthesis.SyntheticFinalization.Result;
+import com.android.tools.r8.synthesis.SyntheticNaming.SyntheticKind;
import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableMap;
-import com.google.common.collect.ImmutableSet;
-import com.google.common.collect.ImmutableSet.Builder;
-import com.google.common.collect.Sets;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
@@ -35,148 +29,114 @@
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Consumer;
import java.util.function.Function;
+import java.util.function.Predicate;
import java.util.function.Supplier;
+import java.util.stream.Collectors;
public class SyntheticItems implements SyntheticDefinitionsProvider {
static final int INVALID_ID_AFTER_SYNTHETIC_FINALIZATION = -1;
- /**
- * The internal synthetic class separator is only used for representing synthetic items during
- * compilation. In particular, this separator must never be used to write synthetic classes to the
- * final compilation result.
- */
- public static final String INTERNAL_SYNTHETIC_CLASS_SEPARATOR = "-$$InternalSynthetic";
-
- /**
- * The external synthetic class separator is used when writing classes. It may appear in types
- * during compilation as the output of a compilation may be the input to another.
- */
- public static final String EXTERNAL_SYNTHETIC_CLASS_SEPARATOR = "-$$ExternalSynthetic";
-
- /** Method prefix when generating synthetic methods in a class. */
- public static final String INTERNAL_SYNTHETIC_METHOD_PREFIX = "m";
-
- public static boolean verifyNotInternalSynthetic(DexType type) {
- assert !type.toDescriptorString().contains(SyntheticItems.INTERNAL_SYNTHETIC_CLASS_SEPARATOR);
- return true;
- }
-
/** Globally incremented id for the next internal synthetic class. */
private int nextSyntheticId;
- /**
- * Thread safe collection of synthesized classes that are not yet committed to the application.
- *
- * <p>TODO(b/158159959): Remove legacy support.
- */
- private final Map<DexType, DexProgramClass> legacyPendingClasses = new ConcurrentHashMap<>();
+ /** Collection of pending items. */
+ private static class PendingSynthetics {
+ /**
+ * Thread safe collection of synthesized classes that are not yet committed to the application.
+ *
+ * <p>TODO(b/158159959): Remove legacy support.
+ */
+ private final Map<DexType, DexProgramClass> legacyClasses = new ConcurrentHashMap<>();
- /**
- * Immutable set of synthetic types in the application (eg, committed).
- *
- * <p>TODO(b/158159959): Remove legacy support.
- */
- private final ImmutableSet<DexType> legacySyntheticTypes;
+ /** Thread safe collection of synthetic items not yet committed to the application. */
+ private final ConcurrentHashMap<DexType, SyntheticDefinition<?, ?, ?>> nonLegacyDefinitions =
+ new ConcurrentHashMap<>();
- /** Thread safe collection of synthetic items not yet committed to the application. */
- private final ConcurrentHashMap<DexType, SyntheticDefinition> pendingDefinitions =
- new ConcurrentHashMap<>();
+ boolean isEmpty() {
+ return legacyClasses.isEmpty() && nonLegacyDefinitions.isEmpty();
+ }
- /** Mapping from synthetic type to its synthetic description. */
- private final ImmutableMap<DexType, SyntheticReference> nonLecacySyntheticItems;
+ boolean containsType(DexType type) {
+ return legacyClasses.containsKey(type) || nonLegacyDefinitions.containsKey(type);
+ }
+
+ boolean verifyNotRewritten(NonIdentityGraphLens lens) {
+ assert legacyClasses.keySet().equals(lens.rewriteTypes(legacyClasses.keySet()));
+ assert nonLegacyDefinitions.keySet().equals(lens.rewriteTypes(nonLegacyDefinitions.keySet()));
+ return true;
+ }
+
+ Collection<DexProgramClass> getAllProgramClasses() {
+ List<DexProgramClass> allPending =
+ new ArrayList<>(nonLegacyDefinitions.size() + legacyClasses.size());
+ for (SyntheticDefinition<?, ?, ?> item : nonLegacyDefinitions.values()) {
+ if (item.isProgramDefinition()) {
+ allPending.add(item.asProgramDefinition().getHolder());
+ }
+ }
+ allPending.addAll(legacyClasses.values());
+ return Collections.unmodifiableList(allPending);
+ }
+ }
+
+ private final CommittedSyntheticsCollection committed;
+
+ private final PendingSynthetics pending = new PendingSynthetics();
// Only for use from initial AppInfo/AppInfoWithClassHierarchy create functions. */
public static CommittedItems createInitialSyntheticItems(DexApplication application) {
return new CommittedItems(
- 0, application, ImmutableSet.of(), ImmutableMap.of(), ImmutableList.of());
+ 0, application, CommittedSyntheticsCollection.empty(), ImmutableList.of());
}
// Only for conversion to a mutable synthetic items collection.
SyntheticItems(CommittedItems commit) {
- this(commit.nextSyntheticId, commit.legacySyntheticTypes, commit.syntheticItems);
+ this(commit.nextSyntheticId, commit.committed);
}
- private SyntheticItems(
- int nextSyntheticId,
- ImmutableSet<DexType> legacySyntheticTypes,
- ImmutableMap<DexType, SyntheticReference> nonLecacySyntheticItems) {
+ private SyntheticItems(int nextSyntheticId, CommittedSyntheticsCollection committed) {
this.nextSyntheticId = nextSyntheticId;
- this.legacySyntheticTypes = legacySyntheticTypes;
- this.nonLecacySyntheticItems = nonLecacySyntheticItems;
- assert Sets.intersection(nonLecacySyntheticItems.keySet(), legacySyntheticTypes).isEmpty();
+ this.committed = committed;
}
public static void collectSyntheticInputs(AppView<AppInfo> appView) {
// Collecting synthetic items must be the very first task after application build.
SyntheticItems synthetics = appView.getSyntheticItems();
assert synthetics.nextSyntheticId == 0;
- assert synthetics.nonLecacySyntheticItems.isEmpty();
- assert !synthetics.hasPendingSyntheticClasses();
+ assert synthetics.committed.isEmpty();
+ assert synthetics.pending.isEmpty();
if (appView.options().intermediate) {
// If the compilation is in intermediate mode the synthetics should just be passed through.
return;
}
- ImmutableMap.Builder<DexType, SyntheticReference> pending = ImmutableMap.builder();
+ CommittedSyntheticsCollection.Builder builder = synthetics.committed.builder();
// TODO(b/158159959): Consider identifying synthetics in the input reader to speed this up.
for (DexProgramClass clazz : appView.appInfo().classes()) {
- DexType annotatedContextType = isSynthesizedMethodsContainer(clazz, appView.dexItemFactory());
- if (annotatedContextType == null) {
- continue;
+ SyntheticMarker marker =
+ SyntheticMarker.stripMarkerFromClass(clazz, appView.dexItemFactory());
+ if (marker.isSyntheticMethods()) {
+ clazz.forEachProgramMethod(
+ // TODO(b/158159959): Support having multiple methods per class.
+ method -> {
+ builder.addNonLegacyMethod(
+ new SyntheticMethodDefinition(marker.getKind(), marker.getContext(), method));
+ });
+ } else if (marker.isSyntheticClass()) {
+ builder.addNonLegacyClass(
+ new SyntheticProgramClassDefinition(marker.getKind(), marker.getContext(), clazz));
}
- clazz.setAnnotations(DexAnnotationSet.empty());
- SynthesizingContext context =
- SynthesizingContext.fromSyntheticInputClass(clazz, annotatedContextType);
- clazz.forEachProgramMethod(
- // TODO(b/158159959): Support having multiple methods per class.
- method -> {
- method.getDefinition().setAnnotations(DexAnnotationSet.empty());
- pending.put(clazz.type, new SyntheticMethodDefinition(context, method).toReference());
- });
}
- pending.putAll(synthetics.nonLecacySyntheticItems);
- ImmutableMap<DexType, SyntheticReference> nonLegacySyntheticItems = pending.build();
- if (nonLegacySyntheticItems.isEmpty()) {
+ CommittedSyntheticsCollection committed = builder.build();
+ if (committed.isEmpty()) {
return;
}
CommittedItems commit =
new CommittedItems(
- synthetics.nextSyntheticId,
- appView.appInfo().app(),
- synthetics.legacySyntheticTypes,
- nonLegacySyntheticItems,
- ImmutableList.of());
+ synthetics.nextSyntheticId, appView.appInfo().app(), committed, ImmutableList.of());
appView.setAppInfo(new AppInfo(commit, appView.appInfo().getMainDexClasses()));
}
- private static DexType isSynthesizedMethodsContainer(
- DexProgramClass clazz, DexItemFactory factory) {
- ClassAccessFlags flags = clazz.accessFlags;
- if (!flags.isSynthetic() || flags.isAbstract() || flags.isEnum()) {
- return null;
- }
- DexType contextType =
- DexAnnotation.getSynthesizedClassAnnotationContextType(clazz.annotations(), factory);
- if (contextType == null) {
- return null;
- }
- if (clazz.superType != factory.objectType) {
- return null;
- }
- if (!clazz.interfaces.isEmpty()) {
- return null;
- }
- if (clazz.annotations().size() != 1) {
- return null;
- }
- for (DexEncodedMethod method : clazz.methods()) {
- if (!SyntheticMethodBuilder.isValidSyntheticMethod(method)) {
- return null;
- }
- }
- return contextType;
- }
-
// Internal synthetic id creation helpers.
private synchronized String getNextSyntheticId() {
@@ -191,49 +151,54 @@
@Override
public DexClass definitionFor(DexType type, Function<DexType, DexClass> baseDefinitionFor) {
- DexProgramClass pending = legacyPendingClasses.get(type);
- if (pending == null) {
- SyntheticDefinition item = pendingDefinitions.get(type);
+ DexClass clazz = pending.legacyClasses.get(type);
+ if (clazz == null) {
+ SyntheticDefinition<?, ?, ?> item = pending.nonLegacyDefinitions.get(type);
if (item != null) {
- pending = item.getHolder();
+ clazz = item.getHolder();
+ assert clazz.isProgramClass() == item.isProgramDefinition();
+ assert clazz.isClasspathClass() == item.isClasspathDefinition();
}
}
- if (pending != null) {
+ if (clazz != null) {
assert baseDefinitionFor.apply(type) == null
: "Pending synthetic definition also present in the active program: " + type;
- return pending;
+ return clazz;
}
return baseDefinitionFor.apply(type);
}
+ public boolean verifyNonLegacySyntheticsAreCommitted() {
+ assert pending.nonLegacyDefinitions.isEmpty()
+ : "Uncommitted synthetics: "
+ + pending.nonLegacyDefinitions.keySet().stream()
+ .map(DexType::getName)
+ .collect(Collectors.joining(", "));
+ return true;
+ }
+
public boolean hasPendingSyntheticClasses() {
- return !legacyPendingClasses.isEmpty() || !pendingDefinitions.isEmpty();
+ return !pending.isEmpty();
}
public Collection<DexProgramClass> getPendingSyntheticClasses() {
- List<DexProgramClass> pending =
- new ArrayList<>(pendingDefinitions.size() + legacyPendingClasses.size());
- for (SyntheticDefinition item : pendingDefinitions.values()) {
- pending.add(item.getHolder());
- }
- pending.addAll(legacyPendingClasses.values());
- return Collections.unmodifiableList(pending);
+ return pending.getAllProgramClasses();
}
private boolean isCommittedSynthetic(DexType type) {
- return nonLecacySyntheticItems.containsKey(type) || legacySyntheticTypes.contains(type);
+ return committed.containsType(type);
}
private boolean isLegacyCommittedSynthetic(DexType type) {
- return legacySyntheticTypes.contains(type);
+ return committed.containsLegacyType(type);
}
public boolean isPendingSynthetic(DexType type) {
- return pendingDefinitions.containsKey(type) || legacyPendingClasses.containsKey(type);
+ return pending.containsType(type);
}
public boolean isLegacyPendingSynthetic(DexType type) {
- return legacyPendingClasses.containsKey(type);
+ return pending.legacyClasses.containsKey(type);
}
public boolean isSyntheticClass(DexType type) {
@@ -247,6 +212,27 @@
return isSyntheticClass(clazz.type);
}
+ // The compiler should not inspect the kind of a synthetic, so this provided only as a assertion
+ // utility.
+ public boolean verifySyntheticLambdaProperty(
+ DexProgramClass clazz,
+ Predicate<DexProgramClass> ifIsLambda,
+ Predicate<DexProgramClass> ifNotLambda) {
+ SyntheticReference<?, ?, ?> reference = committed.getNonLegacyItem(clazz.getType());
+ if (reference == null) {
+ SyntheticDefinition<?, ?, ?> definition = pending.nonLegacyDefinitions.get(clazz.getType());
+ if (definition != null) {
+ reference = definition.toReference();
+ }
+ }
+ if (reference != null && reference.getKind() == SyntheticKind.LAMBDA) {
+ assert ifIsLambda.test(clazz);
+ } else {
+ assert ifNotLambda.test(clazz);
+ }
+ return true;
+ }
+
public boolean isLegacySyntheticClass(DexType type) {
return isLegacyCommittedSynthetic(type) || isLegacyPendingSynthetic(type);
}
@@ -256,18 +242,21 @@
}
public Collection<DexProgramClass> getLegacyPendingClasses() {
- return Collections.unmodifiableCollection(legacyPendingClasses.values());
+ return Collections.unmodifiableCollection(pending.legacyClasses.values());
}
private SynthesizingContext getSynthesizingContext(ProgramDefinition context) {
- SyntheticDefinition pendingItemContext = pendingDefinitions.get(context.getContextType());
- if (pendingItemContext != null) {
- return pendingItemContext.getContext();
+ DexType contextType = context.getContextType();
+ SyntheticDefinition<?, ?, ?> existingDefinition = pending.nonLegacyDefinitions.get(contextType);
+ if (existingDefinition != null) {
+ return existingDefinition.getContext();
}
- SyntheticReference committedItemContext = nonLecacySyntheticItems.get(context.getContextType());
- return committedItemContext != null
- ? committedItemContext.getContext()
- : SynthesizingContext.fromNonSyntheticInputContext(context);
+ SyntheticReference<?, ?, ?> existingReference = committed.getNonLegacyItem(contextType);
+ if (existingReference != null) {
+ return existingReference.getContext();
+ }
+ // This context is not nested in an existing synthetic context so create a new "leaf" context.
+ return SynthesizingContext.fromNonSyntheticInputContext(context);
}
// Addition and creation of synthetic items.
@@ -276,25 +265,71 @@
public void addLegacySyntheticClass(DexProgramClass clazz) {
assert clazz.type.isD8R8SynthesizedClassType();
assert !isCommittedSynthetic(clazz.type);
- DexProgramClass previous = legacyPendingClasses.put(clazz.type, clazz);
+ assert !pending.nonLegacyDefinitions.containsKey(clazz.type);
+ DexProgramClass previous = pending.legacyClasses.put(clazz.type, clazz);
assert previous == null || previous == clazz;
}
+ public DexProgramClass createClass(
+ SyntheticKind kind,
+ DexProgramClass context,
+ DexItemFactory factory,
+ Supplier<String> syntheticIdSupplier,
+ 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.createInternalType(kind, outerContext, syntheticIdSupplier.get(), 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 createClasspathClass(
+ SyntheticKind kind, DexType type, DexClass 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);
+ 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(
- ProgramDefinition context, DexItemFactory factory, Consumer<SyntheticMethodBuilder> fn) {
- return createMethod(context, factory, fn, this::getNextSyntheticId);
+ SyntheticKind kind,
+ ProgramDefinition context,
+ DexItemFactory factory,
+ Consumer<SyntheticMethodBuilder> fn) {
+ return createMethod(kind, context, factory, fn, this::getNextSyntheticId);
}
+ // TODO(b/172194101): Remove this once the uniqueness is a property of the context.
public ProgramMethod createMethod(
+ SyntheticKind kind,
ProgramDefinition context,
DexItemFactory factory,
Consumer<SyntheticMethodBuilder> fn,
MethodProcessingId methodProcessingId) {
- return createMethod(context, factory, fn, methodProcessingId::getFullyQualifiedIdAndIncrement);
+ return createMethod(
+ kind,
+ context,
+ factory,
+ fn,
+ methodProcessingId != null
+ ? methodProcessingId::getFullyQualifiedIdAndIncrement
+ : this::getNextSyntheticId);
}
- private ProgramMethod createMethod(
+ // TODO(b/172194101): Remove/private this once the uniqueness is a property of the context.
+ public ProgramMethod createMethod(
+ SyntheticKind kind,
ProgramDefinition context,
DexItemFactory factory,
Consumer<SyntheticMethodBuilder> fn,
@@ -303,16 +338,21 @@
// 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 = outerContext.createHygienicType(syntheticIdSupplier.get(), factory);
- SyntheticClassBuilder classBuilder = new SyntheticClassBuilder(type, outerContext, factory);
- DexProgramClass clazz = classBuilder.addMethod(fn).build();
+ DexType type =
+ SyntheticNaming.createInternalType(kind, outerContext, syntheticIdSupplier.get(), factory);
+ SyntheticProgramClassBuilder classBuilder =
+ new SyntheticProgramClassBuilder(type, outerContext, factory);
+ DexProgramClass clazz =
+ classBuilder
+ .addMethod(fn.andThen(m -> m.setName(SyntheticNaming.INTERNAL_SYNTHETIC_METHOD_PREFIX)))
+ .build();
ProgramMethod method = new ProgramMethod(clazz, clazz.methods().iterator().next());
- addPendingDefinition(new SyntheticMethodDefinition(outerContext, method));
+ addPendingDefinition(new SyntheticMethodDefinition(kind, outerContext, method));
return method;
}
- private void addPendingDefinition(SyntheticDefinition definition) {
- pendingDefinitions.put(definition.getHolder().getType(), definition);
+ private void addPendingDefinition(SyntheticDefinition<?, ?, ?> definition) {
+ pending.nonLegacyDefinitions.put(definition.getHolder().getType(), definition);
}
// Commit of the synthetic items to a new fully populated application.
@@ -322,125 +362,56 @@
}
public CommittedItems commitPrunedItems(PrunedItems prunedItems) {
- return commit(
- prunedItems.getPrunedApp(),
- prunedItems.getNoLongerSyntheticItems(),
- legacyPendingClasses,
- legacySyntheticTypes,
- pendingDefinitions,
- nonLecacySyntheticItems,
- nextSyntheticId);
+ return commit(prunedItems, pending, committed, nextSyntheticId);
}
public CommittedItems commitRewrittenWithLens(
DexApplication application, NonIdentityGraphLens lens) {
- // Rewrite the previously committed synthetic types.
- ImmutableSet<DexType> rewrittenLegacyTypes = lens.rewriteTypes(this.legacySyntheticTypes);
- ImmutableMap.Builder<DexType, SyntheticReference> rewrittenItems = ImmutableMap.builder();
- for (SyntheticReference reference : nonLecacySyntheticItems.values()) {
- SyntheticReference rewritten = reference.rewrite(lens);
- if (rewritten != null) {
- rewrittenItems.put(rewritten.getHolder(), rewritten);
- }
- }
- // No pending item should need rewriting.
- assert legacyPendingClasses.keySet().equals(lens.rewriteTypes(legacyPendingClasses.keySet()));
- assert pendingDefinitions.keySet().equals(lens.rewriteTypes(pendingDefinitions.keySet()));
+ assert pending.verifyNotRewritten(lens);
return commit(
- application,
- Collections.emptySet(),
- legacyPendingClasses,
- rewrittenLegacyTypes,
- pendingDefinitions,
- rewrittenItems.build(),
- nextSyntheticId);
+ PrunedItems.empty(application), pending, committed.rewriteWithLens(lens), nextSyntheticId);
}
private static CommittedItems commit(
- DexApplication application,
- Set<DexType> removedClasses,
- Map<DexType, DexProgramClass> legacyPendingClasses,
- ImmutableSet<DexType> legacySyntheticTypes,
- ConcurrentHashMap<DexType, SyntheticDefinition> pendingDefinitions,
- ImmutableMap<DexType, SyntheticReference> syntheticItems,
+ PrunedItems prunedItems,
+ PendingSynthetics pending,
+ CommittedSyntheticsCollection committed,
int nextSyntheticId) {
- // Legacy synthetics must already have been committed.
- assert verifyClassesAreInApp(application, legacyPendingClasses.values());
- // Add the set of legacy definitions to the synthetic types.
- ImmutableSet<DexType> mergedLegacyTypes = legacySyntheticTypes;
- if (!legacyPendingClasses.isEmpty() || !removedClasses.isEmpty()) {
- ImmutableSet.Builder<DexType> legacyBuilder = ImmutableSet.builder();
- filteredAdd(legacySyntheticTypes, removedClasses, legacyBuilder);
- filteredAdd(legacyPendingClasses.keySet(), removedClasses, legacyBuilder);
- mergedLegacyTypes = legacyBuilder.build();
- }
- // The set of synthetic items is the union of the previous types plus the pending additions.
- ImmutableMap<DexType, SyntheticReference> mergedItems;
- ImmutableList<DexType> additions;
+ DexApplication application = prunedItems.getPrunedApp();
+ Set<DexType> removedClasses = prunedItems.getNoLongerSyntheticItems();
+ CommittedSyntheticsCollection.Builder builder = committed.builder();
+ // Legacy synthetics must already have been committed to the app.
+ assert verifyClassesAreInApp(application, pending.legacyClasses.values());
+ builder.addLegacyClasses(pending.legacyClasses.values());
+ // Compute the synthetic additions and add them to the application.
+ ImmutableList<DexType> committedProgramTypes;
DexApplication amendedApplication;
- if (pendingDefinitions.isEmpty()) {
- mergedItems = filteredCopy(syntheticItems, removedClasses);
- additions = ImmutableList.of();
+ if (pending.nonLegacyDefinitions.isEmpty()) {
+ committedProgramTypes = ImmutableList.of();
amendedApplication = application;
} else {
DexApplication.Builder<?> appBuilder = application.builder();
- ImmutableMap.Builder<DexType, SyntheticReference> itemsBuilder = ImmutableMap.builder();
- ImmutableList.Builder<DexType> additionsBuilder = ImmutableList.builder();
- for (SyntheticDefinition definition : pendingDefinitions.values()) {
- if (removedClasses.contains(definition.getHolder().getType())) {
- continue;
+ ImmutableList.Builder<DexType> committedProgramTypesBuilder = ImmutableList.builder();
+ for (SyntheticDefinition<?, ?, ?> definition : pending.nonLegacyDefinitions.values()) {
+ if (!removedClasses.contains(definition.getHolder().getType())) {
+ if (definition.isProgramDefinition()) {
+ committedProgramTypesBuilder.add(definition.getHolder().getType());
+ appBuilder.addProgramClass(definition.asProgramDefinition().getHolder());
+ } else {
+ assert definition.isClasspathDefinition();
+ appBuilder.addClasspathClass(definition.asClasspathDefinition().getHolder());
+ }
+ builder.addItem(definition);
}
- SyntheticReference reference = definition.toReference();
- itemsBuilder.put(reference.getHolder(), reference);
- additionsBuilder.add(definition.getHolder().getType());
- appBuilder.addProgramClass(definition.getHolder());
}
- filteredAdd(syntheticItems, removedClasses, itemsBuilder);
- mergedItems = itemsBuilder.build();
- additions = additionsBuilder.build();
+ committedProgramTypes = committedProgramTypesBuilder.build();
amendedApplication = appBuilder.build();
}
return new CommittedItems(
- nextSyntheticId, amendedApplication, mergedLegacyTypes, mergedItems, additions);
- }
-
- private static void filteredAdd(
- Set<DexType> input, Set<DexType> excludeSet, Builder<DexType> result) {
- if (excludeSet.isEmpty()) {
- result.addAll(input);
- } else {
- for (DexType type : input) {
- if (!excludeSet.contains(type)) {
- result.add(type);
- }
- }
- }
- }
-
- private static ImmutableMap<DexType, SyntheticReference> filteredCopy(
- ImmutableMap<DexType, SyntheticReference> syntheticItems, Set<DexType> removedClasses) {
- if (removedClasses.isEmpty()) {
- return syntheticItems;
- }
- ImmutableMap.Builder<DexType, SyntheticReference> builder = ImmutableMap.builder();
- filteredAdd(syntheticItems, removedClasses, builder);
- return builder.build();
- }
-
- private static void filteredAdd(
- ImmutableMap<DexType, SyntheticReference> syntheticItems,
- Set<DexType> removedClasses,
- ImmutableMap.Builder<DexType, SyntheticReference> builder) {
- if (removedClasses.isEmpty()) {
- builder.putAll(syntheticItems);
- } else {
- syntheticItems.forEach(
- (t, r) -> {
- if (!removedClasses.contains(t)) {
- builder.put(t, r);
- }
- });
- }
+ nextSyntheticId,
+ amendedApplication,
+ builder.build().pruneItems(prunedItems),
+ committedProgramTypes);
}
private static boolean verifyClassesAreInApp(
@@ -455,8 +426,6 @@
public Result computeFinalSynthetics(AppView<?> appView) {
assert !hasPendingSyntheticClasses();
- return new SyntheticFinalization(
- appView.options(), legacySyntheticTypes, nonLecacySyntheticItems)
- .computeFinalSynthetics(appView);
+ return new SyntheticFinalization(appView.options(), committed).computeFinalSynthetics(appView);
}
}
diff --git a/src/main/java/com/android/tools/r8/synthesis/SyntheticMarker.java b/src/main/java/com/android/tools/r8/synthesis/SyntheticMarker.java
new file mode 100644
index 0000000..03b7e36
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/synthesis/SyntheticMarker.java
@@ -0,0 +1,96 @@
+// 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.synthesis;
+
+import com.android.tools.r8.graph.ClassAccessFlags;
+import com.android.tools.r8.graph.DexAnnotation;
+import com.android.tools.r8.graph.DexAnnotationSet;
+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.synthesis.SyntheticNaming.SyntheticKind;
+import com.android.tools.r8.utils.Pair;
+
+public class SyntheticMarker {
+
+ public static void addMarkerToClass(
+ DexProgramClass clazz,
+ SyntheticKind kind,
+ SynthesizingContext context,
+ DexItemFactory factory) {
+ clazz.setAnnotations(
+ clazz
+ .annotations()
+ .getWithAddedOrReplaced(
+ DexAnnotation.createAnnotationSynthesizedClass(
+ kind, context.getSynthesizingContextType(), factory)));
+ }
+
+ public static SyntheticMarker stripMarkerFromClass(
+ DexProgramClass clazz, DexItemFactory factory) {
+ SyntheticMarker marker = internalStripMarkerFromClass(clazz, factory);
+ assert marker != NO_MARKER
+ || DexAnnotation.getSynthesizedClassAnnotationContextType(clazz.annotations(), factory)
+ == null;
+ return marker;
+ }
+
+ private static SyntheticMarker internalStripMarkerFromClass(
+ DexProgramClass clazz, DexItemFactory factory) {
+ ClassAccessFlags flags = clazz.accessFlags;
+ if (clazz.superType != factory.objectType) {
+ return NO_MARKER;
+ }
+ if (!flags.isSynthetic() || flags.isAbstract() || flags.isEnum()) {
+ return NO_MARKER;
+ }
+ Pair<SyntheticKind, DexType> info =
+ DexAnnotation.getSynthesizedClassAnnotationContextType(clazz.annotations(), factory);
+ if (info == null) {
+ return NO_MARKER;
+ }
+ assert clazz.annotations().size() == 1;
+ SyntheticKind kind = info.getFirst();
+ DexType context = info.getSecond();
+ if (kind.isSingleSyntheticMethod) {
+ if (!clazz.interfaces.isEmpty()) {
+ return NO_MARKER;
+ }
+ for (DexEncodedMethod method : clazz.methods()) {
+ if (!SyntheticMethodBuilder.isValidSyntheticMethod(method)) {
+ return NO_MARKER;
+ }
+ }
+ }
+ clazz.setAnnotations(DexAnnotationSet.empty());
+ return new SyntheticMarker(kind, SynthesizingContext.fromSyntheticInputClass(clazz, context));
+ }
+
+ private static final SyntheticMarker NO_MARKER = new SyntheticMarker(null, null);
+
+ private final SyntheticKind kind;
+ private final SynthesizingContext context;
+
+ public SyntheticMarker(SyntheticKind kind, SynthesizingContext context) {
+ this.kind = kind;
+ this.context = context;
+ }
+
+ public boolean isSyntheticMethods() {
+ return kind != null && kind.isSingleSyntheticMethod;
+ }
+
+ public boolean isSyntheticClass() {
+ return kind != null && !kind.isSingleSyntheticMethod;
+ }
+
+ public SyntheticKind getKind() {
+ return kind;
+ }
+
+ public SynthesizingContext getContext() {
+ return context;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/synthesis/SyntheticMethodBuilder.java b/src/main/java/com/android/tools/r8/synthesis/SyntheticMethodBuilder.java
index 9fcc7d9..ab4cd49 100644
--- a/src/main/java/com/android/tools/r8/synthesis/SyntheticMethodBuilder.java
+++ b/src/main/java/com/android/tools/r8/synthesis/SyntheticMethodBuilder.java
@@ -9,6 +9,7 @@
import com.android.tools.r8.graph.DexEncodedMethod;
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.GenericSignature.MethodTypeSignature;
import com.android.tools.r8.graph.MethodAccessFlags;
import com.android.tools.r8.graph.ParameterAnnotationsList;
@@ -19,16 +20,26 @@
Code generate(DexMethod method);
}
- private final SyntheticClassBuilder parent;
- private final String name;
+ private final SyntheticClassBuilder<?, ?> parent;
+ private DexString name = null;
private DexProto proto = null;
private CfVersion classFileVersion;
private SyntheticCodeGenerator codeGenerator = null;
private MethodAccessFlags accessFlags = null;
- SyntheticMethodBuilder(SyntheticClassBuilder parent, String name) {
+ SyntheticMethodBuilder(SyntheticClassBuilder<?, ?> parent) {
this.parent = parent;
+ }
+
+ public SyntheticMethodBuilder setName(String name) {
+ return setName(parent.getFactory().createString(name));
+ }
+
+ public SyntheticMethodBuilder setName(DexString name) {
+ assert name != null;
+ assert this.name == null;
this.name = name;
+ return this;
}
public SyntheticMethodBuilder setProto(DexProto proto) {
@@ -52,6 +63,7 @@
}
DexEncodedMethod build() {
+ assert name != null;
boolean isCompilerSynthesized = true;
DexMethod methodSignature = getMethodSignature();
DexEncodedMethod method =
diff --git a/src/main/java/com/android/tools/r8/synthesis/SyntheticMethodDefinition.java b/src/main/java/com/android/tools/r8/synthesis/SyntheticMethodDefinition.java
index ecc1a81..87393f7 100644
--- a/src/main/java/com/android/tools/r8/synthesis/SyntheticMethodDefinition.java
+++ b/src/main/java/com/android/tools/r8/synthesis/SyntheticMethodDefinition.java
@@ -3,78 +3,74 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.synthesis;
-import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.ProgramMethod;
+import com.android.tools.r8.synthesis.SyntheticNaming.SyntheticKind;
import com.android.tools.r8.utils.structural.RepresentativeMap;
-import com.google.common.hash.HashCode;
import com.google.common.hash.Hasher;
-import com.google.common.hash.Hashing;
-import java.util.Comparator;
+import java.util.function.Consumer;
/**
* Definition of a synthetic method item.
*
* <p>This class is internal to the synthetic items collection, thus package-protected.
*/
-class SyntheticMethodDefinition extends SyntheticDefinition
- implements Comparable<SyntheticMethodDefinition> {
+class SyntheticMethodDefinition
+ extends SyntheticDefinition<
+ SyntheticMethodReference, SyntheticMethodDefinition, DexProgramClass>
+ implements SyntheticProgramDefinition {
private final ProgramMethod method;
- SyntheticMethodDefinition(SynthesizingContext context, ProgramMethod method) {
- super(context);
+ SyntheticMethodDefinition(SyntheticKind kind, SynthesizingContext context, ProgramMethod method) {
+ super(kind, context);
this.method = method;
}
+ @Override
+ public void apply(
+ Consumer<SyntheticMethodDefinition> onMethod,
+ Consumer<SyntheticProgramClassDefinition> onClass) {
+ onMethod.accept(this);
+ }
+
public ProgramMethod getMethod() {
return method;
}
@Override
- SyntheticReference toReference() {
- return new SyntheticMethodReference(getContext(), method.getReference());
+ public boolean isProgramDefinition() {
+ return true;
}
@Override
- DexProgramClass getHolder() {
+ public SyntheticProgramDefinition asProgramDefinition() {
+ return this;
+ }
+
+ @Override
+ SyntheticMethodReference toReference() {
+ return new SyntheticMethodReference(getKind(), getContext(), method.getReference());
+ }
+
+ @Override
+ public DexProgramClass getHolder() {
return method.getHolder();
}
@Override
- HashCode computeHash(RepresentativeMap map, boolean intermediate) {
- Hasher hasher = Hashing.sha256().newHasher();
- if (intermediate) {
- // If in intermediate mode, include the context type as sharing is restricted to within a
- // single context.
- hasher.putInt(getContext().getSynthesizingContextType().hashCode());
- }
- method.getDefinition().hashSyntheticContent(hasher, map);
- return hasher.hash();
+ void internalComputeHash(Hasher hasher, RepresentativeMap map) {
+ method.getDefinition().hashWithTypeEquivalence(hasher, map);
}
@Override
- boolean isEquivalentTo(SyntheticDefinition other, boolean intermediate) {
- if (!(other instanceof SyntheticMethodDefinition)) {
- return false;
- }
- if (intermediate
- && getContext().getSynthesizingContextType()
- != other.getContext().getSynthesizingContextType()) {
- // If in intermediate mode, only synthetics within the same context should be considered
- // equal.
- return false;
- }
- SyntheticMethodDefinition o = (SyntheticMethodDefinition) other;
- return method.getDefinition().isSyntheticContentEqual(o.method.getDefinition());
+ int internalCompareTo(SyntheticMethodDefinition other, RepresentativeMap map) {
+ return method.getDefinition().compareWithTypeEquivalenceTo(other.method.getDefinition(), map);
}
- // Since methods are sharable they must define an order from which representatives can be found.
@Override
- public int compareTo(SyntheticMethodDefinition other) {
- return Comparator.comparing(SyntheticMethodDefinition::getContext)
- .thenComparing(m -> m.method.getDefinition(), DexEncodedMethod::syntheticCompareTo)
- .compare(this, other);
+ public boolean isValid() {
+ return SyntheticMethodBuilder.isValidSyntheticMethod(method.getDefinition());
}
@Override
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 28913d0..09a0f1a 100644
--- a/src/main/java/com/android/tools/r8/synthesis/SyntheticMethodReference.java
+++ b/src/main/java/com/android/tools/r8/synthesis/SyntheticMethodReference.java
@@ -5,10 +5,12 @@
import com.android.tools.r8.graph.DexClass;
import com.android.tools.r8.graph.DexMethod;
-import com.android.tools.r8.graph.DexReference;
+import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.GraphLens.NonIdentityGraphLens;
import com.android.tools.r8.graph.ProgramMethod;
+import com.android.tools.r8.synthesis.SyntheticNaming.SyntheticKind;
+import java.util.function.Consumer;
import java.util.function.Function;
/**
@@ -16,37 +18,36 @@
*
* <p>This class is internal to the synthetic items collection, thus package-protected.
*/
-class SyntheticMethodReference extends SyntheticReference {
+class SyntheticMethodReference
+ extends SyntheticReference<SyntheticMethodReference, SyntheticMethodDefinition, DexProgramClass>
+ implements SyntheticProgramReference {
final DexMethod method;
- SyntheticMethodReference(SynthesizingContext context, DexMethod method) {
- super(context);
+ SyntheticMethodReference(SyntheticKind kind, SynthesizingContext context, DexMethod method) {
+ super(kind, context);
this.method = method;
}
@Override
- DexReference getReference() {
- return method;
- }
-
- @Override
DexType getHolder() {
return method.holder;
}
@Override
- SyntheticDefinition lookupDefinition(Function<DexType, DexClass> definitions) {
+ SyntheticMethodDefinition lookupDefinition(Function<DexType, DexClass> definitions) {
DexClass clazz = definitions.apply(method.holder);
if (clazz == null) {
return null;
}
assert clazz.isProgramClass();
ProgramMethod definition = clazz.asProgramClass().lookupProgramMethod(method);
- return definition != null ? new SyntheticMethodDefinition(getContext(), definition) : null;
+ return definition != null
+ ? new SyntheticMethodDefinition(getKind(), getContext(), definition)
+ : null;
}
@Override
- SyntheticReference rewrite(NonIdentityGraphLens lens) {
+ SyntheticMethodReference rewrite(NonIdentityGraphLens lens) {
DexMethod rewritten = lens.lookupMethod(method);
// If the reference has been non-trivially rewritten the compiler has changed it and it can no
// longer be considered a synthetic. The context may or may not have changed.
@@ -54,12 +55,29 @@
// If the referenced item is rewritten, it should be moved to another holder as the
// synthetic holder is no longer part of the synthetic collection.
assert method.holder != rewritten.holder;
- assert SyntheticItems.verifyNotInternalSynthetic(rewritten.holder);
+ assert SyntheticNaming.verifyNotInternalSynthetic(rewritten.holder);
return null;
}
SynthesizingContext context = getContext().rewrite(lens);
- return context == getContext() && rewritten == method
- ? this
- : new SyntheticMethodReference(context, rewritten);
+ if (context == getContext() && rewritten == method) {
+ return this;
+ }
+ // Ensure that if a synthetic moves its context moves consistently.
+ if (method != rewritten) {
+ context =
+ SynthesizingContext.fromSyntheticContextChange(
+ rewritten.holder, context, lens.dexItemFactory());
+ if (context == null) {
+ return null;
+ }
+ }
+ return new SyntheticMethodReference(getKind(), context, rewritten);
+ }
+
+ @Override
+ public void apply(
+ Consumer<SyntheticMethodReference> onMethod,
+ Consumer<SyntheticProgramClassReference> onClass) {
+ onMethod.accept(this);
}
}
diff --git a/src/main/java/com/android/tools/r8/synthesis/SyntheticNaming.java b/src/main/java/com/android/tools/r8/synthesis/SyntheticNaming.java
new file mode 100644
index 0000000..b33e260
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/synthesis/SyntheticNaming.java
@@ -0,0 +1,170 @@
+// 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.synthesis;
+
+import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.references.ClassReference;
+import com.android.tools.r8.references.Reference;
+import com.android.tools.r8.utils.DescriptorUtils;
+
+public class SyntheticNaming {
+
+ /**
+ * Enumeration of all kinds of synthetic items.
+ *
+ * <p>The synthetic kinds are used to provide hinting about what a synthetic item represents. The
+ * kinds must *not* be used be the compiler and are only meant for "debugging". The compiler and
+ * its test may use the kind information as part of asserting properties of the compiler. The kind
+ * will be put into any non-minified synthetic name and thus the kind "descriptor" must be a
+ * distinct for each kind.
+ */
+ public enum SyntheticKind {
+ // Class synthetics.
+ COMPANION_CLASS("CompanionClass", false),
+ LAMBDA("Lambda", false),
+ // Method synthetics.
+ BACKPORT("Backport", true),
+ STATIC_INTERFACE_CALL("StaticInterfaceCall", true),
+ TO_STRING_IF_NOT_NULL("ToStringIfNotNull", true),
+ THROW_CCE_IF_NOT_NULL("ThrowCCEIfNotNull", true),
+ THROW_ICCE("ThrowICCE", true),
+ THROW_NSME("ThrowNSME", true),
+ TWR_CLOSE_RESOURCE("TwrCloseResource", true),
+ SERVICE_LOADER("ServiceLoad", true);
+
+ public final String descriptor;
+ public final boolean isSingleSyntheticMethod;
+
+ SyntheticKind(String descriptor, boolean isSingleSyntheticMethod) {
+ this.descriptor = descriptor;
+ this.isSingleSyntheticMethod = isSingleSyntheticMethod;
+ }
+
+ public static SyntheticKind fromDescriptor(String descriptor) {
+ for (SyntheticKind kind : values()) {
+ if (kind.descriptor.equals(descriptor)) {
+ return kind;
+ }
+ }
+ return null;
+ }
+ }
+
+ /**
+ * The internal synthetic class separator is only used for representing synthetic items during
+ * compilation. In particular, this separator must never be used to write synthetic classes to the
+ * final compilation result.
+ */
+ private static final String INTERNAL_SYNTHETIC_CLASS_SEPARATOR = "-$$InternalSynthetic";
+ /**
+ * The external synthetic class separator is used when writing classes. It may appear in types
+ * during compilation as the output of a compilation may be the input to another.
+ */
+ private static final String EXTERNAL_SYNTHETIC_CLASS_SEPARATOR = "-$$ExternalSynthetic";
+ /** Method prefix when generating synthetic methods in a class. */
+ static final String INTERNAL_SYNTHETIC_METHOD_PREFIX = "m";
+
+ // TODO(b/158159959): Remove usage of name-based identification.
+ public static boolean isSyntheticName(String typeName) {
+ return typeName.contains(INTERNAL_SYNTHETIC_CLASS_SEPARATOR)
+ || typeName.contains(EXTERNAL_SYNTHETIC_CLASS_SEPARATOR);
+ }
+
+ static DexType createInternalType(
+ SyntheticKind kind, SynthesizingContext context, String id, DexItemFactory factory) {
+ return createType(
+ INTERNAL_SYNTHETIC_CLASS_SEPARATOR,
+ kind,
+ context.getSynthesizingContextType(),
+ id,
+ factory);
+ }
+
+ static DexType createExternalType(
+ SyntheticKind kind, DexType context, String id, DexItemFactory factory) {
+ return createType(EXTERNAL_SYNTHETIC_CLASS_SEPARATOR, kind, context, id, factory);
+ }
+
+ private static DexType createType(
+ String separator, SyntheticKind kind, DexType context, String id, DexItemFactory factory) {
+ return factory.createType(createDescriptor(separator, kind, context.getInternalName(), id));
+ }
+
+ private static String createDescriptor(
+ String separator, SyntheticKind kind, String context, String id) {
+ return DescriptorUtils.getDescriptorFromClassBinaryName(
+ context + separator + kind.descriptor + id);
+ }
+
+ public static boolean verifyNotInternalSynthetic(DexType type) {
+ return verifyNotInternalSynthetic(type.toDescriptorString());
+ }
+
+ public static boolean verifyNotInternalSynthetic(ClassReference reference) {
+ return verifyNotInternalSynthetic(reference.getDescriptor());
+ }
+
+ public static boolean verifyNotInternalSynthetic(String typeBinaryNameOrDescriptor) {
+ assert !typeBinaryNameOrDescriptor.contains(INTERNAL_SYNTHETIC_CLASS_SEPARATOR);
+ return true;
+ }
+
+ // Visible via package protection in SyntheticItemsTestUtils.
+
+ enum Phase {
+ INTERNAL,
+ EXTERNAL
+ }
+
+ static String getPhaseSeparator(Phase phase) {
+ assert phase != null;
+ return phase == Phase.INTERNAL
+ ? INTERNAL_SYNTHETIC_CLASS_SEPARATOR
+ : EXTERNAL_SYNTHETIC_CLASS_SEPARATOR;
+ }
+
+ static ClassReference makeSyntheticReferenceForTest(
+ ClassReference context, SyntheticKind kind, String id) {
+ return Reference.classFromDescriptor(
+ createDescriptor(EXTERNAL_SYNTHETIC_CLASS_SEPARATOR, kind, context.getBinaryName(), id));
+ }
+
+ static boolean isSynthetic(ClassReference clazz, Phase phase, SyntheticKind kind) {
+ String typeName = clazz.getTypeName();
+ String separator = getPhaseSeparator(phase);
+ int i = typeName.indexOf(separator);
+ return i >= 0 && checkMatchFrom(kind, typeName, i, separator, phase == Phase.EXTERNAL);
+ }
+
+ private static boolean checkMatchFrom(
+ SyntheticKind kind,
+ String name,
+ int i,
+ String externalSyntheticClassSeparator,
+ boolean checkIntSuffix) {
+ int end = i + externalSyntheticClassSeparator.length() + kind.descriptor.length();
+ if (end >= name.length()) {
+ return false;
+ }
+ String prefix = name.substring(i, end);
+ return prefix.equals(externalSyntheticClassSeparator + kind.descriptor)
+ && (!checkIntSuffix || isInt(name.substring(end)));
+ }
+
+ private static boolean isInt(String str) {
+ if (str.isEmpty()) {
+ return false;
+ }
+ if ('0' == str.charAt(0)) {
+ return str.length() == 1;
+ }
+ for (int i = 0; i < str.length(); i++) {
+ if (!Character.isDigit(str.charAt(i))) {
+ return false;
+ }
+ }
+ return true;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/synthesis/SyntheticProgramClassBuilder.java b/src/main/java/com/android/tools/r8/synthesis/SyntheticProgramClassBuilder.java
new file mode 100644
index 0000000..6408621
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/synthesis/SyntheticProgramClassBuilder.java
@@ -0,0 +1,27 @@
+// 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.synthesis;
+
+import com.android.tools.r8.graph.ClassKind;
+import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.graph.DexType;
+
+public class SyntheticProgramClassBuilder
+ extends SyntheticClassBuilder<SyntheticProgramClassBuilder, DexProgramClass> {
+
+ SyntheticProgramClassBuilder(DexType type, SynthesizingContext context, DexItemFactory factory) {
+ super(type, context, factory);
+ }
+
+ @Override
+ public ClassKind<DexProgramClass> getClassKind() {
+ return ClassKind.PROGRAM;
+ }
+
+ @Override
+ public SyntheticProgramClassBuilder self() {
+ return this;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/synthesis/SyntheticProgramClassDefinition.java b/src/main/java/com/android/tools/r8/synthesis/SyntheticProgramClassDefinition.java
new file mode 100644
index 0000000..8b42eb6
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/synthesis/SyntheticProgramClassDefinition.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.synthesis;
+
+import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.synthesis.SyntheticNaming.SyntheticKind;
+import com.android.tools.r8.utils.structural.RepresentativeMap;
+import com.google.common.hash.Hasher;
+import java.util.function.Consumer;
+
+/**
+ * Definition of a synthetic class item.
+ *
+ * <p>This class is internal to the synthetic items collection, thus package-protected.
+ */
+class SyntheticProgramClassDefinition
+ extends SyntheticClassDefinition<
+ SyntheticProgramClassReference, SyntheticProgramClassDefinition, DexProgramClass>
+ implements SyntheticProgramDefinition {
+
+ SyntheticProgramClassDefinition(
+ SyntheticKind kind, SynthesizingContext context, DexProgramClass clazz) {
+ super(kind, context, clazz);
+ }
+
+ @Override
+ public void apply(
+ Consumer<SyntheticMethodDefinition> onMethod,
+ Consumer<SyntheticProgramClassDefinition> onClass) {
+ onClass.accept(this);
+ }
+
+ @Override
+ public boolean isProgramDefinition() {
+ return true;
+ }
+
+ @Override
+ public SyntheticProgramDefinition asProgramDefinition() {
+ return this;
+ }
+
+ @Override
+ SyntheticProgramClassReference toReference() {
+ return new SyntheticProgramClassReference(getKind(), getContext(), clazz.getType());
+ }
+
+ @Override
+ public boolean isValid() {
+ return clazz.isPublic() && clazz.isFinal() && clazz.accessFlags.isSynthetic();
+ }
+
+ @Override
+ void internalComputeHash(Hasher hasher, RepresentativeMap map) {
+ clazz.hashWithTypeEquivalence(hasher, map);
+ }
+
+ @Override
+ int internalCompareTo(SyntheticProgramClassDefinition o, RepresentativeMap map) {
+ return clazz.compareWithTypeEquivalenceTo(o.clazz, map);
+ }
+
+ @Override
+ public String toString() {
+ return "SyntheticProgramClass{ clazz = "
+ + clazz.type.toSourceString()
+ + ", kind = "
+ + getKind()
+ + ", context = "
+ + getContext()
+ + " }";
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/synthesis/SyntheticProgramClassReference.java b/src/main/java/com/android/tools/r8/synthesis/SyntheticProgramClassReference.java
new file mode 100644
index 0000000..3472268
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/synthesis/SyntheticProgramClassReference.java
@@ -0,0 +1,70 @@
+// 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.synthesis;
+
+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.GraphLens.NonIdentityGraphLens;
+import com.android.tools.r8.synthesis.SyntheticNaming.SyntheticKind;
+import java.util.function.Consumer;
+import java.util.function.Function;
+
+/**
+ * Reference to a synthetic class item.
+ *
+ * <p>This class is internal to the synthetic items collection, thus package-protected.
+ */
+class SyntheticProgramClassReference
+ extends SyntheticClassReference<
+ SyntheticProgramClassReference, SyntheticProgramClassDefinition, DexProgramClass>
+ implements SyntheticProgramReference {
+
+ SyntheticProgramClassReference(SyntheticKind kind, SynthesizingContext context, DexType type) {
+ super(kind, context, type);
+ }
+
+ @Override
+ SyntheticProgramClassDefinition lookupDefinition(Function<DexType, DexClass> definitions) {
+ DexClass clazz = definitions.apply(type);
+ if (clazz == null) {
+ return null;
+ }
+ assert clazz.isProgramClass();
+ return new SyntheticProgramClassDefinition(getKind(), getContext(), clazz.asProgramClass());
+ }
+
+ @Override
+ SyntheticProgramClassReference rewrite(NonIdentityGraphLens lens) {
+ DexType rewritten = lens.lookupType(type);
+ // If the reference has been non-trivially rewritten the compiler has changed it and it can no
+ // longer be considered a synthetic. The context may or may not have changed.
+ if (type != rewritten && !lens.isSimpleRenaming(type, rewritten)) {
+ // If the referenced item is rewritten, it should be moved to another holder as the
+ // synthetic holder is no longer part of the synthetic collection.
+ assert SyntheticNaming.verifyNotInternalSynthetic(rewritten);
+ return null;
+ }
+ SynthesizingContext context = getContext().rewrite(lens);
+ if (context == getContext() && rewritten == type) {
+ return this;
+ }
+ // Ensure that if a synthetic moves its context moves consistently.
+ if (type != rewritten) {
+ context =
+ SynthesizingContext.fromSyntheticContextChange(rewritten, context, lens.dexItemFactory());
+ if (context == null) {
+ return null;
+ }
+ }
+ return new SyntheticProgramClassReference(getKind(), context, rewritten);
+ }
+
+ @Override
+ public void apply(
+ Consumer<SyntheticMethodReference> onMethod,
+ Consumer<SyntheticProgramClassReference> onClass) {
+ onClass.accept(this);
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/synthesis/SyntheticProgramDefinition.java b/src/main/java/com/android/tools/r8/synthesis/SyntheticProgramDefinition.java
new file mode 100644
index 0000000..dd8953a
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/synthesis/SyntheticProgramDefinition.java
@@ -0,0 +1,17 @@
+// 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.synthesis;
+
+import com.android.tools.r8.graph.DexProgramClass;
+import java.util.function.Consumer;
+
+public interface SyntheticProgramDefinition {
+
+ void apply(
+ Consumer<SyntheticMethodDefinition> onMethod,
+ Consumer<SyntheticProgramClassDefinition> onClass);
+
+ DexProgramClass getHolder();
+}
diff --git a/src/main/java/com/android/tools/r8/synthesis/SyntheticProgramReference.java b/src/main/java/com/android/tools/r8/synthesis/SyntheticProgramReference.java
new file mode 100644
index 0000000..4df1f46
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/synthesis/SyntheticProgramReference.java
@@ -0,0 +1,14 @@
+// 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.synthesis;
+
+import java.util.function.Consumer;
+
+public interface SyntheticProgramReference {
+
+ void apply(
+ Consumer<SyntheticMethodReference> onMethod,
+ Consumer<SyntheticProgramClassReference> onClass);
+}
diff --git a/src/main/java/com/android/tools/r8/synthesis/SyntheticReference.java b/src/main/java/com/android/tools/r8/synthesis/SyntheticReference.java
index 6618378..1a2d2fb 100644
--- a/src/main/java/com/android/tools/r8/synthesis/SyntheticReference.java
+++ b/src/main/java/com/android/tools/r8/synthesis/SyntheticReference.java
@@ -4,9 +4,9 @@
package com.android.tools.r8.synthesis;
import com.android.tools.r8.graph.DexClass;
-import com.android.tools.r8.graph.DexReference;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.GraphLens.NonIdentityGraphLens;
+import com.android.tools.r8.synthesis.SyntheticNaming.SyntheticKind;
import java.util.function.Function;
/**
@@ -14,16 +14,26 @@
*
* <p>This class is internal to the synthetic items collection, thus package-protected.
*/
-abstract class SyntheticReference {
+abstract class SyntheticReference<
+ R extends SyntheticReference<R, D, C>,
+ D extends SyntheticDefinition<R, D, C>,
+ C extends DexClass> {
+
+ private final SyntheticKind kind;
private final SynthesizingContext context;
- SyntheticReference(SynthesizingContext context) {
+ SyntheticReference(SyntheticKind kind, SynthesizingContext context) {
+ assert kind != null;
+ assert context != null;
+ this.kind = kind;
this.context = context;
}
- abstract SyntheticDefinition lookupDefinition(Function<DexType, DexClass> definitions);
+ abstract D lookupDefinition(Function<DexType, DexClass> definitions);
- abstract DexReference getReference();
+ final SyntheticKind getKind() {
+ return kind;
+ }
final SynthesizingContext getContext() {
return context;
@@ -31,5 +41,5 @@
abstract DexType getHolder();
- abstract SyntheticReference rewrite(NonIdentityGraphLens lens);
+ abstract R rewrite(NonIdentityGraphLens lens);
}
diff --git a/src/main/java/com/android/tools/r8/utils/BooleanBox.java b/src/main/java/com/android/tools/r8/utils/BooleanBox.java
index f46ac1c..f6b790d 100644
--- a/src/main/java/com/android/tools/r8/utils/BooleanBox.java
+++ b/src/main/java/com/android/tools/r8/utils/BooleanBox.java
@@ -4,6 +4,8 @@
package com.android.tools.r8.utils;
+import java.util.function.BooleanSupplier;
+
public class BooleanBox {
private boolean value;
@@ -14,10 +16,24 @@
set(initialValue);
}
+ public void computeIfNotSet(BooleanSupplier supplier) {
+ if (isFalse()) {
+ set(supplier.getAsBoolean());
+ }
+ }
+
public boolean get() {
return value;
}
+ public boolean isFalse() {
+ return !get();
+ }
+
+ public boolean isTrue() {
+ return get();
+ }
+
public void set() {
set(true);
}
diff --git a/src/main/java/com/android/tools/r8/utils/ClasspathClassCollection.java b/src/main/java/com/android/tools/r8/utils/ClasspathClassCollection.java
index 28eb17b..e14d8ed 100644
--- a/src/main/java/com/android/tools/r8/utils/ClasspathClassCollection.java
+++ b/src/main/java/com/android/tools/r8/utils/ClasspathClassCollection.java
@@ -10,10 +10,19 @@
/** Represents a collection of classpath classes. */
public class ClasspathClassCollection extends ClassMap<DexClasspathClass> {
+
+ private ClasspathClassCollection() {
+ this(null);
+ }
+
public ClasspathClassCollection(ClassProvider<DexClasspathClass> classProvider) {
super(null, classProvider);
}
+ public static ClasspathClassCollection empty() {
+ return new ClasspathClassCollection();
+ }
+
@Override
DexClasspathClass resolveClassConflict(DexClasspathClass a, DexClasspathClass b) {
throw new CompilationError("Classpath type already present: " + a.type.toSourceString());
diff --git a/src/main/java/com/android/tools/r8/utils/ExceptionUtils.java b/src/main/java/com/android/tools/r8/utils/ExceptionUtils.java
index 8c4da03..ef5c3ca 100644
--- a/src/main/java/com/android/tools/r8/utils/ExceptionUtils.java
+++ b/src/main/java/com/android/tools/r8/utils/ExceptionUtils.java
@@ -22,9 +22,20 @@
import java.util.function.BiFunction;
import java.util.function.Consumer;
import java.util.function.Supplier;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
public abstract class ExceptionUtils {
+ public static String getMainStackTrace() {
+ return Thread.getAllStackTraces().entrySet().stream()
+ .filter(x -> x.getKey().getName().equals("main"))
+ .map(x -> x.getValue())
+ .flatMap(x -> Stream.of(x))
+ .map(x -> x.toString())
+ .collect(Collectors.joining(System.lineSeparator()));
+ }
+
public static void withConsumeResourceHandler(
Reporter reporter, StringConsumer consumer, String data) {
withConsumeResourceHandler(reporter, handler -> consumer.accept(data, handler));
diff --git a/src/main/java/com/android/tools/r8/utils/FilteredArchiveClassFileProvider.java b/src/main/java/com/android/tools/r8/utils/FilteredArchiveClassFileProvider.java
index 43cbb4d..483315e 100644
--- a/src/main/java/com/android/tools/r8/utils/FilteredArchiveClassFileProvider.java
+++ b/src/main/java/com/android/tools/r8/utils/FilteredArchiveClassFileProvider.java
@@ -10,6 +10,6 @@
class FilteredArchiveClassFileProvider extends InternalArchiveClassFileProvider {
FilteredArchiveClassFileProvider(FilteredClassPath archive) throws IOException {
- super(archive.getPath(), entry -> archive.matchesFile(entry));
+ super(archive.getPath(), archive::matchesFile);
}
}
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 db914a4..a8bec4a 100644
--- a/src/main/java/com/android/tools/r8/utils/InternalOptions.java
+++ b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
@@ -3,7 +3,6 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.utils;
-import static com.google.common.base.Predicates.not;
import com.android.tools.r8.ClassFileConsumer;
import com.android.tools.r8.CompilationMode;
@@ -32,10 +31,11 @@
import com.android.tools.r8.features.FeatureSplitConfiguration;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexClass;
-import com.android.tools.r8.graph.DexDefinition;
+import com.android.tools.r8.graph.DexClasspathClass;
import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.graph.DexItem;
import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.graph.DexLibraryClass;
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.DexType;
@@ -48,6 +48,7 @@
import com.android.tools.r8.ir.code.IRCode;
import com.android.tools.r8.ir.conversion.MethodProcessingId;
import com.android.tools.r8.ir.desugar.DesugaredLibraryConfiguration;
+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;
@@ -73,13 +74,7 @@
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
-import java.io.IOException;
import java.io.PrintStream;
-import java.lang.reflect.GenericSignatureFormatError;
-import java.nio.file.Files;
-import java.nio.file.InvalidPathException;
-import java.nio.file.Path;
-import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
@@ -307,6 +302,7 @@
// the actual catch handler allowed when inlining. Threshold found empirically by testing on
// GMS Core.
public int inliningControlFlowResolutionBlocksThreshold = 15;
+ public boolean enableSwitchRewriting = true;
public boolean enableStringSwitchConversion = true;
public int minimumStringSwitchSize = 3;
public boolean enableEnumValueOptimization = true;
@@ -492,7 +488,7 @@
if (testing.enableForceNestBasedAccessDesugaringForTest) {
return true;
}
- return enableNestBasedAccessDesugaring && !canUseNestBasedAccess();
+ return !canUseNestBasedAccess();
}
public Set<String> extensiveLoggingFilter = getExtensiveLoggingFilter();
@@ -513,10 +509,6 @@
public boolean enableLambdaMerging = false;
// Flag to turn on/off desugaring in D8/R8.
public DesugarState desugarState = DesugarState.ON;
- // Flag to turn on/off desugaring of invoke-special to a virtual method on the current class.
- public boolean enableInvokeSpecialToVirtualMethodDesugaring = true;
- // Flag to turn on/off JDK11+ nest-access control
- public boolean enableNestBasedAccessDesugaring = true;
// Flag to turn on/off reduction of nest to improve class merging optimizations.
public boolean enableNestReduction = true;
// Defines interface method rewriter behavior.
@@ -611,6 +603,7 @@
public boolean printCfg = false;
public String printCfgFile;
public boolean ignoreMissingClasses = false;
+
// EXPERIMENTAL flag to get behaviour as close to Proguard as possible.
public boolean forceProguardCompatibility = false;
public AssertionConfigurationWithDefault assertionsConfiguration = null;
@@ -680,19 +673,6 @@
return ImmutableSet.of();
}
- private static Set<String> getExtensiveFieldMinifierLoggingFilter() {
- String property =
- System.getProperty("com.android.tools.r8.extensiveFieldMinifierLoggingFilter");
- if (property != null) {
- ImmutableSet.Builder<String> builder = ImmutableSet.builder();
- for (String method : property.split(";")) {
- builder.add(method);
- }
- return builder.build();
- }
- return ImmutableSet.of();
- }
-
private static Set<String> getExtensiveInterfaceMethodMinifierLoggingFilter() {
String property =
System.getProperty("com.android.tools.r8.extensiveInterfaceMethodMinifierLoggingFilter");
@@ -706,40 +686,6 @@
return ImmutableSet.of();
}
- private static Set<String> getNullableReceiverInliningFilter() {
- String property = System.getProperty("com.android.tools.r8.nullableReceiverInliningFilter");
- if (property != null) {
- // The property is allowed to be either (1) a path to a file where each line is a method
- // signature, or (2) a semicolon separated list of method signatures.
- Path path = null;
- try {
- Path tmp = Paths.get(property);
- if (Files.exists(tmp)) {
- path = tmp;
- }
- } catch (InvalidPathException | NullPointerException e) {
- // Ignore, treat as a semicolon separated list of method signatures.
- }
- ImmutableSet.Builder<String> builder = ImmutableSet.builder();
- if (path != null) {
- try {
- Files.readAllLines(path).stream()
- .map(String::trim)
- .filter(not(String::isEmpty))
- .forEach(builder::add);
- } catch (IOException e) {
- throw new RuntimeException(e);
- }
- } else {
- for (String method : property.split(";")) {
- builder.add(method);
- }
- }
- return builder.build();
- }
- return ImmutableSet.of();
- }
-
public static class InvalidParameterAnnotationInfo {
final DexMethod method;
@@ -845,121 +791,64 @@
private final Set<DexItem> invalidLibraryClasses = Sets.newConcurrentHashSet();
- public void errorMissingClassMissingNestHost(DexClass compiledClass) {
- throw reporter.fatalError(messageErrorMissingNestHost(compiledClass));
- }
-
- public void warningMissingClassMissingNestHost(DexClass compiledClass) {
- if (compiledClass.isLibraryClass()) {
- errorMissingClassMissingNestHost(compiledClass);
- }
- reporter.warning(new StringDiagnostic(messageWarningMissingNestHost(compiledClass)));
- }
-
- public void nestDesugaringWarningMissingNestHost(DexClass compiledClass) {
- if (compiledClass.isLibraryClass()) {
- errorMissingClassMissingNestHost(compiledClass);
- }
- reporter.warning(
+ public RuntimeException errorMissingNestHost(DexClass clazz) {
+ throw reporter.fatalError(
new MissingNestHostNestDesugarDiagnostic(
- compiledClass.getOrigin(),
- Position.UNKNOWN,
- messageWarningMissingNestHost(compiledClass)));
+ clazz.getOrigin(), Position.UNKNOWN, messageErrorMissingNestHost(clazz)));
}
- public void errorMissingClassIncompleteNest(List<DexType> nest, AppView<?> appView) {
- throw reporter.fatalError(messageErrorIncompleteNest(nest, appView));
- }
-
- public void warningMissingClassIncompleteNest(List<DexType> nest, AppView<?> appView) {
- for (DexType type : nest) {
- DexClass clazz = appView.definitionFor(type);
- if (clazz != null && clazz.isLibraryClass()) {
- errorMissingClassIncompleteNest(nest, appView);
- return;
- }
- }
- reporter.warning(new StringDiagnostic(messageWarningIncompleteNest(nest, appView)));
- }
-
- public void nestDesugaringWarningIncompleteNest(List<DexType> nest, AppView<?> appView) {
- DexClass availableClass = null;
- for (DexType type : nest) {
- DexClass clazz = appView.definitionFor(type);
- if (clazz != null && clazz.isProgramClass()) {
- availableClass = clazz;
- } else if (clazz != null && clazz.isLibraryClass()) {
- errorMissingClassIncompleteNest(nest, appView);
- return;
- }
- }
- assert availableClass != null;
- reporter.warning(
- new IncompleteNestNestDesugarDiagnosic(
- availableClass.getOrigin(),
- Position.UNKNOWN,
- messageWarningIncompleteNest(nest, appView)));
- }
-
- private String messageErrorMissingNestHost(DexClass compiledClass) {
+ private static String messageErrorMissingNestHost(DexClass compiledClass) {
String nestHostName = compiledClass.getNestHost().getName();
return "Class "
+ compiledClass.type.getName()
+ " requires its nest host "
+ nestHostName
- + " to be on program or class path. ";
+ + " to be on program or class path.";
}
- private String messageWarningMissingNestHost(DexClass compiledClass) {
- return messageErrorMissingNestHost(compiledClass)
- + "Class "
- + compiledClass.type.getName()
- + " is considered as not being part of any nest.";
+ public RuntimeException errorMissingNestMember(Nest nest) {
+ throw reporter.fatalError(
+ new IncompleteNestNestDesugarDiagnosic(
+ nest.getHostClass().getOrigin(), Position.UNKNOWN, messageErrorIncompleteNest(nest)));
}
- private String messageErrorIncompleteNest(List<DexType> nest, AppView<?> appView) {
- List<String> programClassesFromNest = new ArrayList<>();
- List<String> unavailableClasses = new ArrayList<>();
- List<String> classPathClasses = new ArrayList<>();
- List<String> libraryClasses = new ArrayList<>();
- for (DexType type : nest) {
- DexClass clazz = appView.definitionFor(appView.graphLens().lookupType(type));
- if (clazz == null) {
- unavailableClasses.add(type.getName());
- } else if (clazz.isLibraryClass()) {
- libraryClasses.add(type.getName());
- } else if (clazz.isProgramClass()) {
- programClassesFromNest.add(type.getName());
- } else {
- assert clazz.isClasspathClass();
- classPathClasses.add(type.getName());
- }
+ private static String messageErrorIncompleteNest(Nest nest) {
+ List<DexProgramClass> programClassesFromNest = new ArrayList<>();
+ List<DexClasspathClass> classpathClassesFromNest = new ArrayList<>();
+ List<DexLibraryClass> libraryClassesFromNest = new ArrayList<>();
+ nest.getHostClass()
+ .accept(
+ programClassesFromNest::add,
+ classpathClassesFromNest::add,
+ libraryClassesFromNest::add);
+ for (DexClass memberClass : nest.getMembers()) {
+ memberClass.accept(
+ programClassesFromNest::add, classpathClassesFromNest::add, libraryClassesFromNest::add);
}
StringBuilder stringBuilder =
new StringBuilder("Compilation of classes ")
- .append(String.join(", ", programClassesFromNest))
+ .append(StringUtils.join(", ", programClassesFromNest, DexClass::getTypeName))
.append(" requires its nest mates ");
- if (!unavailableClasses.isEmpty()) {
- stringBuilder.append(String.join(", ", unavailableClasses)).append(" (unavailable) ");
+ if (nest.hasMissingMembers()) {
+ stringBuilder
+ .append(StringUtils.join(", ", nest.getMissingMembers(), DexType::getTypeName))
+ .append(" (unavailable) ");
}
- if (!libraryClasses.isEmpty()) {
- stringBuilder.append(String.join(", ", unavailableClasses)).append(" (on library path) ");
+ if (!libraryClassesFromNest.isEmpty()) {
+ stringBuilder
+ .append(StringUtils.join(", ", libraryClassesFromNest, DexClass::getTypeName))
+ .append(" (on library path) ");
}
stringBuilder.append("to be on program or class path.");
- if (!classPathClasses.isEmpty()) {
+ if (!classpathClassesFromNest.isEmpty()) {
stringBuilder
.append("(Classes ")
- .append(String.join(", ", classPathClasses))
+ .append(StringUtils.join(", ", classpathClassesFromNest, DexClass::getTypeName))
.append(" from the same nest are on class path).");
}
return stringBuilder.toString();
}
- private String messageWarningIncompleteNest(List<DexType> nest, AppView<?> appView) {
- return messageErrorIncompleteNest(nest, appView)
- + " Unavailable classes are considered as not being part of the nest.";
- }
-
public void warningMissingTypeForDesugar(
Origin origin, Position position, DexType missingType, DexMethod context) {
if (reportedMissingForDesugaring.add(missingType)) {
@@ -1034,31 +923,6 @@
}
}
- public void warningInvalidSignature(
- DexDefinition item, Origin origin, String signature, GenericSignatureFormatError e) {
- StringBuilder message = new StringBuilder("Invalid signature '");
- message.append(signature);
- message.append("' for ");
- if (item.isDexClass()) {
- message.append("class ");
- message.append((item.asDexClass()).getType().toSourceString());
- } else if (item.isDexEncodedField()) {
- message.append("field ");
- message.append(item.toSourceString());
- } else {
- assert item.isDexEncodedMethod();
- message.append("method ");
- message.append(item.toSourceString());
- }
- message.append(".");
- message.append(System.lineSeparator());
- message.append("Signature is ignored and will not be present in the output.");
- message.append(System.lineSeparator());
- message.append("Parser error: ");
- message.append(e.getMessage());
- reporter.warning(new StringDiagnostic(message.toString(), origin));
- }
-
private final Box<Boolean> reportedExperimentClassFileVersion = new Box<>(false);
public void warningExperimentalClassFileVersion(Origin origin) {
@@ -1280,6 +1144,7 @@
public boolean enable = true;
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;
@@ -1429,14 +1294,15 @@
public boolean addCallEdgesForLibraryInvokes = false;
public boolean allowCheckDiscardedErrors = false;
+ public boolean allowDexInputForTesting = false;
public boolean allowInjectedAnnotationMethods = false;
public boolean allowTypeErrors =
!Version.isDevelopmentVersion()
|| System.getProperty("com.android.tools.r8.allowTypeErrors") != null;
public boolean allowInvokeErrors = false;
+ public boolean allowUnnecessaryDontWarnWildcards = true;
+ public boolean allowUnusedDontWarnRules = true;
public boolean disableL8AnnotationRemoval = false;
- public boolean allowClassInlinerGracefulExit =
- System.getProperty("com.android.tools.r8.disallowClassInlinerGracefulExit") == null;
public boolean reportUnusedProguardConfigurationRules = false;
public boolean alwaysUseExistingAccessInfoCollectionsInMemberRebinding = true;
public boolean alwaysUsePessimisticRegisterAllocation = false;
@@ -1458,7 +1324,6 @@
public Consumer<IRCode> irModifier = null;
public int basicBlockMuncherIterationLimit = NO_LIMIT;
public boolean dontReportFailingCheckDiscarded = false;
- public boolean deterministicSortingBasedOnDexType = true;
public PrintStream whyAreYouNotInliningConsumer = System.out;
public boolean trackDesugaredAPIConversions =
System.getProperty("com.android.tools.r8.trackDesugaredAPIConversions") != null;
@@ -1472,6 +1337,10 @@
public boolean disableMappingToOriginalProgramVerification = false;
public boolean allowInvalidCfAccessFlags =
System.getProperty("com.android.tools.r8.allowInvalidCfAccessFlags") != null;
+ // TODO(b/177333791): Set to true
+ public boolean checkForNotExpandingMainDexTracingResult = false;
+
+ public boolean allowConflictingSyntheticTypes = false;
// Flag to allow processing of resources in D8. A data resource consumer still needs to be
// specified.
@@ -1603,6 +1472,16 @@
return !isDesugaring() || hasMinApi(AndroidApiLevel.K);
}
+ public boolean enableTryWithResourcesDesugaring() {
+ switch (tryWithResourcesDesugaring) {
+ case Off:
+ return false;
+ case Auto:
+ return !canUseSuppressedExceptions();
+ }
+ throw new Unreachable();
+ }
+
public boolean canUsePrivateInterfaceMethods() {
return !isDesugaring() || hasMinApi(AndroidApiLevel.N);
}
@@ -1622,6 +1501,10 @@
&& !canUseDefaultAndStaticInterfaceMethods();
}
+ public boolean isSwitchRewritingEnabled() {
+ return enableSwitchRewriting && !debug;
+ }
+
public boolean isStringSwitchConversionEnabled() {
return enableStringSwitchConversion && !debug;
}
@@ -2009,4 +1892,11 @@
public boolean canHaveDalvikIntUsedAsNonIntPrimitiveTypeBug() {
return isGeneratingClassFiles() || minApiLevel < AndroidApiLevel.L.getLevel();
}
+
+ // The standard library prior to API 19 did not contain a ZipFile that implemented Closable.
+ //
+ // See b/177532008.
+ public boolean canHaveZipFileWithMissingCloseableBug() {
+ return isGeneratingClassFiles() || minApiLevel < AndroidApiLevel.K.getLevel();
+ }
}
diff --git a/src/main/java/com/android/tools/r8/utils/IterableUtils.java b/src/main/java/com/android/tools/r8/utils/IterableUtils.java
index cd1c0a0..2b0267d 100644
--- a/src/main/java/com/android/tools/r8/utils/IterableUtils.java
+++ b/src/main/java/com/android/tools/r8/utils/IterableUtils.java
@@ -59,10 +59,14 @@
return -1;
}
- public static <T> Iterable<T> filter(Iterable<T> iterable, Predicate<T> predicate) {
+ public static <T> Iterable<T> filter(Iterable<T> iterable, Predicate<? super T> predicate) {
return () -> IteratorUtils.filter(iterable.iterator(), predicate);
}
+ public static <T> boolean hasNext(Iterable<T> iterable) {
+ return iterable.iterator().hasNext();
+ }
+
public static <T> int size(Iterable<T> iterable) {
int result = 0;
for (T element : iterable) {
@@ -77,6 +81,10 @@
return result;
}
+ public static <S, T> Iterable<T> transform(Iterable<S> iterable, Function<S, T> fn) {
+ return Iterables.transform(iterable, fn::apply);
+ }
+
public static <T> boolean isEmpty(Iterable<T> iterable) {
return !iterable.iterator().hasNext();
}
@@ -102,13 +110,13 @@
}
public static <F> int sumInt(Iterable<F> iterable, Function<? super F, Integer> fn) {
- Iterable<Integer> integers = Iterables.transform(iterable, i -> fn.apply(i));
+ Iterable<Integer> integers = Iterables.transform(iterable, fn::apply);
return sumInt(integers);
}
public static <T, U> Iterable<U> flatMap(
Iterable<T> iterable, Function<? super T, Iterable<U>> map) {
- return Iterables.concat(Iterables.transform(iterable, val -> map.apply(val)));
+ return Iterables.concat(Iterables.transform(iterable, map::apply));
}
public static <T> Iterable<T> empty() {
diff --git a/src/main/java/com/android/tools/r8/utils/ListUtils.java b/src/main/java/com/android/tools/r8/utils/ListUtils.java
index 40ae9ce..e6b0085 100644
--- a/src/main/java/com/android/tools/r8/utils/ListUtils.java
+++ b/src/main/java/com/android/tools/r8/utils/ListUtils.java
@@ -14,6 +14,36 @@
public class ListUtils {
+ /**
+ * Maps each element in the list using on the given function. Returns the mapped list if any
+ * elements were rewritten, otherwise returns the original list.
+ *
+ * <p>If the given function {@param fn} returns null for an element {@code v}, this is interpreted
+ * as the singleton list containing {@code v} (i.e., no changes should be made to the given
+ * element).
+ */
+ public static <T> List<T> flatMap(List<T> list, Function<T, List<T>> fn, List<T> defaultValue) {
+ List<T> result = null;
+ for (int i = 0; i < list.size(); i++) {
+ T element = list.get(i);
+ List<T> replacement = fn.apply(element);
+ if (replacement == null) {
+ if (result != null) {
+ result.add(element);
+ }
+ } else {
+ if (result == null) {
+ result = new ArrayList<>(list.size() + replacement.size() - 1);
+ for (int j = 0; j < i; j++) {
+ result.add(list.get(j));
+ }
+ }
+ result.addAll(replacement);
+ }
+ }
+ return result != null ? result : defaultValue;
+ }
+
public static <T> T first(List<T> list) {
return list.get(0);
}
@@ -40,6 +70,14 @@
return -1;
}
+ public static <S, T> List<T> map(Iterable<S> list, Function<S, T> fn) {
+ List<T> result = new ArrayList<>();
+ for (S element : list) {
+ result.add(fn.apply(element));
+ }
+ return result;
+ }
+
public static <S, T> List<T> map(Collection<S> list, Function<S, T> fn) {
List<T> result = new ArrayList<>(list.size());
for (S element : list) {
diff --git a/src/main/java/com/android/tools/r8/utils/MapUtils.java b/src/main/java/com/android/tools/r8/utils/MapUtils.java
index ac79586..abae424 100644
--- a/src/main/java/com/android/tools/r8/utils/MapUtils.java
+++ b/src/main/java/com/android/tools/r8/utils/MapUtils.java
@@ -45,6 +45,6 @@
public static String toString(Map<?, ?> map) {
return StringUtils.join(
- map.entrySet(), ",", BraceType.TUBORG, entry -> entry.getKey() + ":" + entry.getValue());
+ ",", map.entrySet(), entry -> entry.getKey() + ":" + entry.getValue(), BraceType.TUBORG);
}
}
diff --git a/src/main/java/com/android/tools/r8/utils/Pair.java b/src/main/java/com/android/tools/r8/utils/Pair.java
index ac58f56..c2f3489 100644
--- a/src/main/java/com/android/tools/r8/utils/Pair.java
+++ b/src/main/java/com/android/tools/r8/utils/Pair.java
@@ -50,4 +50,9 @@
public boolean equals(Object obj) {
throw new Unreachable("Pair does not want to support equality!");
}
+
+ @Override
+ public String toString() {
+ return "Pair{" + first + ", " + second + '}';
+ }
}
diff --git a/src/main/java/com/android/tools/r8/utils/StringUtils.java b/src/main/java/com/android/tools/r8/utils/StringUtils.java
index a90d2bc..8f398ac 100644
--- a/src/main/java/com/android/tools/r8/utils/StringUtils.java
+++ b/src/main/java/com/android/tools/r8/utils/StringUtils.java
@@ -108,8 +108,8 @@
return append(builder, collection, ", ", BraceType.PARENS);
}
- public static <T> StringBuilder append(StringBuilder builder, Collection<T> collection,
- String seperator, BraceType brace) {
+ public static <T> StringBuilder append(
+ StringBuilder builder, Iterable<T> collection, String seperator, BraceType brace) {
builder.append(brace.left());
boolean first = true;
for (T element : collection) {
@@ -128,18 +128,22 @@
return join(collection, separator, BraceType.NONE);
}
+ public static <T> String join(String separator, Iterable<T> iterable, Function<T, String> fn) {
+ return join(separator, iterable, fn, BraceType.NONE);
+ }
+
public static String join(String separator, String... strings) {
return join(Arrays.asList(strings), separator, BraceType.NONE);
}
public static <T> String join(Collection<T> collection, String separator, BraceType brace) {
- return join(collection, separator, brace, Object::toString);
+ return join(separator, collection, Object::toString, brace);
}
- public static <T> String join(Collection<T> collection, String separator, BraceType brace,
- Function<T, String> fn) {
+ public static <T> String join(
+ String separator, Iterable<T> iterable, Function<T, String> fn, BraceType brace) {
StringBuilder builder = new StringBuilder();
- append(builder, ListUtils.map(collection, fn), separator, brace);
+ append(builder, IterableUtils.transform(iterable, fn), separator, brace);
return builder.toString();
}
diff --git a/src/main/java/com/android/tools/r8/utils/ThreadUtils.java b/src/main/java/com/android/tools/r8/utils/ThreadUtils.java
index 2a16dbf..a8fa794 100644
--- a/src/main/java/com/android/tools/r8/utils/ThreadUtils.java
+++ b/src/main/java/com/android/tools/r8/utils/ThreadUtils.java
@@ -8,6 +8,7 @@
import java.util.Iterator;
import java.util.List;
import java.util.Map;
+import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
@@ -18,6 +19,11 @@
public static final int NOT_SPECIFIED = -1;
+ public static <T> Future<T> processAsynchronously(
+ Callable<T> callable, ExecutorService executorService) {
+ return executorService.submit(callable);
+ }
+
public static <T, R, E extends Exception> Collection<R> processItemsWithResults(
Iterable<T> items, ThrowingFunction<T, R, E> consumer, ExecutorService executorService)
throws ExecutionException {
diff --git a/src/main/java/com/android/tools/r8/utils/structural/CompareToVisitor.java b/src/main/java/com/android/tools/r8/utils/structural/CompareToVisitor.java
index a0b2104..90e3b16 100644
--- a/src/main/java/com/android/tools/r8/utils/structural/CompareToVisitor.java
+++ b/src/main/java/com/android/tools/r8/utils/structural/CompareToVisitor.java
@@ -37,10 +37,7 @@
public final <S extends StructuralItem<S>> int visitItemCollection(
Collection<S> items1, Collection<S> items2) {
- return visitItemIterator(
- items1.iterator(),
- items2.iterator(),
- (s, other, visitor) -> s.acceptCompareTo(other, visitor));
+ return visitItemIterator(items1.iterator(), items2.iterator(), StructuralItem::acceptCompareTo);
}
public abstract int visitDexString(DexString string1, DexString string2);
diff --git a/src/main/java/com/android/tools/r8/utils/structural/CompareToVisitorBase.java b/src/main/java/com/android/tools/r8/utils/structural/CompareToVisitorBase.java
index 234732c..c721599 100644
--- a/src/main/java/com/android/tools/r8/utils/structural/CompareToVisitorBase.java
+++ b/src/main/java/com/android/tools/r8/utils/structural/CompareToVisitorBase.java
@@ -18,29 +18,39 @@
/** Base class to share most visiting methods */
public abstract class CompareToVisitorBase extends CompareToVisitor {
+ private static boolean DEBUG = false;
+
+ // Helper to debug insert a breakpoint on order values.
+ public static int debug(int order) {
+ if (DEBUG && order != 0) {
+ return order;
+ }
+ return order;
+ }
+
@Override
public final int visitBool(boolean value1, boolean value2) {
- return Boolean.compare(value1, value2);
+ return debug(Boolean.compare(value1, value2));
}
@Override
public final int visitInt(int value1, int value2) {
- return Integer.compare(value1, value2);
+ return debug(Integer.compare(value1, value2));
}
@Override
public int visitLong(long value1, long value2) {
- return Long.compare(value1, value2);
+ return debug(Long.compare(value1, value2));
}
@Override
public int visitFloat(float value1, float value2) {
- return Float.compare(value1, value2);
+ return debug(Float.compare(value1, value2));
}
@Override
public int visitDouble(double value1, double value2) {
- return Double.compare(value1, value2);
+ return debug(Double.compare(value1, value2));
}
@Override
@@ -53,12 +63,12 @@
if (order == 0) {
order = visitBool(it1.hasNext(), it2.hasNext());
}
- return order;
+ return debug(order);
}
@Override
public int visitDexString(DexString string1, DexString string2) {
- return string1.compareTo(string2);
+ return debug(string1.compareTo(string2));
}
@Override
@@ -74,19 +84,19 @@
order = visitDexMethod(reference1.asDexMethod(), reference2.asDexMethod());
}
}
- return order;
+ return debug(order);
}
@Override
public final <S> int visit(S item1, S item2, Comparator<S> comparator) {
- return comparator.compare(item1, item2);
+ return debug(comparator.compare(item1, item2));
}
@Override
public final <S> int visit(S item1, S item2, StructuralMapping<S> accept) {
ItemSpecification<S> itemVisitor = new ItemSpecification<>(item1, item2, this);
accept.apply(itemVisitor);
- return itemVisitor.order;
+ return debug(itemVisitor.order);
}
private static class ItemSpecification<T>
@@ -198,7 +208,7 @@
}
@Override
- protected <S> ItemSpecification<T> withItemIterator(
+ protected <S> ItemSpecification<T> withCustomItemIterator(
Function<T, Iterator<S>> getter, CompareToAccept<S> compare, HashingAccept<S> hasher) {
if (order == 0) {
order = parent.visitItemIterator(getter.apply(item1), getter.apply(item2), compare);
diff --git a/src/main/java/com/android/tools/r8/utils/structural/CompareToVisitorWithNamingLens.java b/src/main/java/com/android/tools/r8/utils/structural/CompareToVisitorWithNamingLens.java
index f581065..7569929 100644
--- a/src/main/java/com/android/tools/r8/utils/structural/CompareToVisitorWithNamingLens.java
+++ b/src/main/java/com/android/tools/r8/utils/structural/CompareToVisitorWithNamingLens.java
@@ -29,34 +29,35 @@
@Override
public int visitDexType(DexType type1, DexType type2) {
- return namingLens
- .lookupDescriptor(type1)
- .acceptCompareTo(namingLens.lookupDescriptor(type2), this);
+ return debug(
+ namingLens
+ .lookupDescriptor(type1)
+ .acceptCompareTo(namingLens.lookupDescriptor(type2), this));
}
@Override
public int visitDexField(DexField field1, DexField field2) {
int order = field1.holder.acceptCompareTo(field2.holder, this);
if (order != 0) {
- return order;
+ return debug(order);
}
order = namingLens.lookupName(field1).acceptCompareTo(namingLens.lookupName(field2), this);
if (order != 0) {
- return order;
+ return debug(order);
}
- return field1.type.acceptCompareTo(field2.type, this);
+ return debug(field1.type.acceptCompareTo(field2.type, this));
}
@Override
public int visitDexMethod(DexMethod method1, DexMethod method2) {
int order = method1.holder.acceptCompareTo(method2.holder, this);
if (order != 0) {
- return order;
+ return debug(order);
}
order = namingLens.lookupName(method1).acceptCompareTo(namingLens.lookupName(method2), this);
if (order != 0) {
- return order;
+ return debug(order);
}
- return method1.proto.acceptCompareTo(method2.proto, this);
+ return debug(method1.proto.acceptCompareTo(method2.proto, this));
}
}
diff --git a/src/main/java/com/android/tools/r8/utils/structural/CompareToVisitorWithTypeEquivalence.java b/src/main/java/com/android/tools/r8/utils/structural/CompareToVisitorWithTypeEquivalence.java
index 89bd02b..ccb5c9e 100644
--- a/src/main/java/com/android/tools/r8/utils/structural/CompareToVisitorWithTypeEquivalence.java
+++ b/src/main/java/com/android/tools/r8/utils/structural/CompareToVisitorWithTypeEquivalence.java
@@ -28,6 +28,6 @@
public int visitDexType(DexType type1, DexType type2) {
DexType repr1 = representatives.getRepresentative(type1);
DexType repr2 = representatives.getRepresentative(type2);
- return repr1.getDescriptor().acceptCompareTo(repr2.getDescriptor(), this);
+ return debug(repr1.getDescriptor().acceptCompareTo(repr2.getDescriptor(), this));
}
}
diff --git a/src/main/java/com/android/tools/r8/utils/structural/HashCodeVisitor.java b/src/main/java/com/android/tools/r8/utils/structural/HashCodeVisitor.java
index 892183d..244d404 100644
--- a/src/main/java/com/android/tools/r8/utils/structural/HashCodeVisitor.java
+++ b/src/main/java/com/android/tools/r8/utils/structural/HashCodeVisitor.java
@@ -105,7 +105,7 @@
}
@Override
- protected <S> HashCodeVisitor<T> withItemIterator(
+ protected <S> HashCodeVisitor<T> withCustomItemIterator(
Function<T, Iterator<S>> getter, CompareToAccept<S> compare, HashingAccept<S> hasher) {
Iterator<S> it = getter.apply(item);
while (it.hasNext()) {
diff --git a/src/main/java/com/android/tools/r8/utils/structural/HashingVisitorWithTypeEquivalence.java b/src/main/java/com/android/tools/r8/utils/structural/HashingVisitorWithTypeEquivalence.java
index e63ac6c..6c388fb 100644
--- a/src/main/java/com/android/tools/r8/utils/structural/HashingVisitorWithTypeEquivalence.java
+++ b/src/main/java/com/android/tools/r8/utils/structural/HashingVisitorWithTypeEquivalence.java
@@ -170,7 +170,7 @@
}
@Override
- protected <S> ItemSpecification<T> withItemIterator(
+ protected <S> ItemSpecification<T> withCustomItemIterator(
Function<T, Iterator<S>> getter, CompareToAccept<S> compare, HashingAccept<S> hasher) {
parent.visitItemIterator(getter.apply(item), hasher);
return this;
diff --git a/src/main/java/com/android/tools/r8/utils/structural/StructuralSpecification.java b/src/main/java/com/android/tools/r8/utils/structural/StructuralSpecification.java
index a81fc18..69d8796 100644
--- a/src/main/java/com/android/tools/r8/utils/structural/StructuralSpecification.java
+++ b/src/main/java/com/android/tools/r8/utils/structural/StructuralSpecification.java
@@ -51,12 +51,12 @@
HashingAccept<S> hasher);
/** Base implementation for visiting an enumeration of items. */
- protected abstract <S> V withItemIterator(
+ protected abstract <S> V withCustomItemIterator(
Function<T, Iterator<S>> getter, CompareToAccept<S> compare, HashingAccept<S> hasher);
public final <S> V withCustomItemCollection(
Function<T, Collection<S>> getter, StructuralAcceptor<S> acceptor) {
- return withItemIterator(getter.andThen(Collection::iterator), acceptor, acceptor);
+ return withCustomItemIterator(getter.andThen(Collection::iterator), acceptor, acceptor);
}
/**
@@ -79,24 +79,23 @@
predicate, getter, StructuralItem::acceptCompareTo, StructuralItem::acceptHashing);
}
+ public final <S extends StructuralItem<S>> V withItemIterator(Function<T, Iterator<S>> getter) {
+ return withCustomItemIterator(
+ getter, StructuralItem::acceptCompareTo, StructuralItem::acceptHashing);
+ }
+
public final <S extends StructuralItem<S>> V withItemCollection(
Function<T, Collection<S>> getter) {
- return withItemIterator(
- getter.andThen(Collection::iterator),
- StructuralItem::acceptCompareTo,
- StructuralItem::acceptHashing);
+ return withItemIterator(getter.andThen(Collection::iterator));
}
public final <S extends StructuralItem<S>> V withItemArray(Function<T, S[]> getter) {
- return withItemIterator(
- getter.andThen(a -> Arrays.asList(a).iterator()),
- StructuralItem::acceptCompareTo,
- StructuralItem::acceptHashing);
+ return withItemIterator(getter.andThen(a -> Arrays.asList(a).iterator()));
}
public final <S extends StructuralItem<S>> V withItemArrayAllowingNullMembers(
Function<T, S[]> getter) {
- return withItemIterator(
+ return withCustomItemIterator(
getter.andThen(a -> Arrays.asList(a).iterator()),
(a, b, visitor) -> {
if (a == null || b == null) {
diff --git a/src/main/keep.txt b/src/main/keep.txt
index 82f6965..24d0fbc 100644
--- a/src/main/keep.txt
+++ b/src/main/keep.txt
@@ -26,3 +26,8 @@
# Compatibility command line program used by the Android Platform build.
-keep public class com.android.tools.r8.compatproguard.CompatProguard { public static void main(java.lang.String[]); }
+
+# TODO(b/176783536): Avoid need to use -dontwarn.
+-dontwarn com.google.errorprone.annotations.**
+-dontwarn com.google.j2objc.annotations.*
+-dontwarn javax.annotation.Nullable
diff --git a/src/test/examplesAndroidO/multidex004/ref-list-1.txt b/src/test/examplesAndroidO/multidex004/ref-list-1.txt
index ac6e888..c817c33 100644
--- a/src/test/examplesAndroidO/multidex004/ref-list-1.txt
+++ b/src/test/examplesAndroidO/multidex004/ref-list-1.txt
@@ -1,4 +1,4 @@
-Lmultidex004/-$$Lambda$MainActivity$g120D_43GXrTOaB3kfYt6wSIJh4;
+Lmultidex004/MainActivity-$$ExternalSyntheticLambda0;
Lmultidex004/MainActivity;
Lmultidex004/VersionInterface;
Lmultidex004/VersionStatic;
diff --git a/src/test/java/com/android/tools/r8/D8IncrementalRunExamplesAndroidOTest.java b/src/test/java/com/android/tools/r8/D8IncrementalRunExamplesAndroidOTest.java
index d25c465..82cfebf 100644
--- a/src/test/java/com/android/tools/r8/D8IncrementalRunExamplesAndroidOTest.java
+++ b/src/test/java/com/android/tools/r8/D8IncrementalRunExamplesAndroidOTest.java
@@ -13,9 +13,9 @@
import com.android.tools.r8.errors.Unimplemented;
import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.ir.desugar.InterfaceMethodRewriter;
-import com.android.tools.r8.ir.desugar.LambdaRewriter;
-import com.android.tools.r8.ir.desugar.TwrCloseResourceRewriter;
-import com.android.tools.r8.synthesis.SyntheticItems;
+import com.android.tools.r8.references.ClassReference;
+import com.android.tools.r8.references.Reference;
+import com.android.tools.r8.synthesis.SyntheticItemsTestUtils;
import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.AndroidApp;
import com.android.tools.r8.utils.DescriptorUtils;
@@ -93,11 +93,12 @@
for (String descriptor : descriptors) {
// classes are either lambda classes used by the main class, companion classes of the main
// interface, the main class/interface, or for JDK9, desugaring of try-with-resources.
+ ClassReference reference = Reference.classFromDescriptor(descriptor);
Assert.assertTrue(
- descriptor.contains(LambdaRewriter.LAMBDA_CLASS_NAME_PREFIX)
- || descriptor.endsWith(InterfaceMethodRewriter.COMPANION_CLASS_NAME_SUFFIX + ";")
- || descriptor.equals(TwrCloseResourceRewriter.UTILITY_CLASS_DESCRIPTOR)
- || descriptor.contains(SyntheticItems.EXTERNAL_SYNTHETIC_CLASS_SEPARATOR)
+ descriptor.endsWith(InterfaceMethodRewriter.COMPANION_CLASS_NAME_SUFFIX + ";")
+ || SyntheticItemsTestUtils.isExternalTwrCloseMethod(reference)
+ || SyntheticItemsTestUtils.isExternalLambda(reference)
+ || SyntheticItemsTestUtils.isExternalStaticInterfaceCall(reference)
|| descriptor.equals(mainClassDescriptor));
}
String classDescriptor =
diff --git a/src/test/java/com/android/tools/r8/D8LazyRunExamplesAndroidOTest.java b/src/test/java/com/android/tools/r8/D8LazyRunExamplesAndroidOTest.java
index 1d9ec50..78bbcf3 100644
--- a/src/test/java/com/android/tools/r8/D8LazyRunExamplesAndroidOTest.java
+++ b/src/test/java/com/android/tools/r8/D8LazyRunExamplesAndroidOTest.java
@@ -92,9 +92,7 @@
// Build each class individually using tmpClassesDir as classpath for desugaring.
List<ProgramResource> individalDexes = new ArrayList<>();
List<Path> individualClassFiles =
- Files.walk(tmpClassesDir)
- .filter(classFile -> FileUtils.isClassFile(classFile))
- .collect(Collectors.toList());
+ Files.walk(tmpClassesDir).filter(FileUtils::isClassFile).collect(Collectors.toList());
for (Path classFile : individualClassFiles) {
D8Command.Builder builder =
D8Command.builder()
diff --git a/src/test/java/com/android/tools/r8/D8TestCompileResult.java b/src/test/java/com/android/tools/r8/D8TestCompileResult.java
index 1fcd0e7..b5fccb8 100644
--- a/src/test/java/com/android/tools/r8/D8TestCompileResult.java
+++ b/src/test/java/com/android/tools/r8/D8TestCompileResult.java
@@ -4,7 +4,9 @@
package com.android.tools.r8;
import com.android.tools.r8.ToolHelper.ProcessResult;
+import com.android.tools.r8.errors.Unimplemented;
import com.android.tools.r8.utils.AndroidApp;
+import java.util.Set;
public class D8TestCompileResult extends TestCompileResult<D8TestCompileResult, D8TestRunResult> {
D8TestCompileResult(TestState state, AndroidApp app, int minApiLevel, OutputMode outputMode) {
@@ -22,6 +24,11 @@
}
@Override
+ public Set<String> getMainDexClasses() {
+ throw new Unimplemented();
+ }
+
+ @Override
public String getStdout() {
return state.getStdout();
}
diff --git a/src/test/java/com/android/tools/r8/DXTestCompileResult.java b/src/test/java/com/android/tools/r8/DXTestCompileResult.java
index 4a0c42f..a3cf644 100644
--- a/src/test/java/com/android/tools/r8/DXTestCompileResult.java
+++ b/src/test/java/com/android/tools/r8/DXTestCompileResult.java
@@ -6,6 +6,7 @@
import com.android.tools.r8.ToolHelper.ProcessResult;
import com.android.tools.r8.errors.Unimplemented;
import com.android.tools.r8.utils.AndroidApp;
+import java.util.Set;
public class DXTestCompileResult extends TestCompileResult<DXTestCompileResult, DXTestRunResult> {
@@ -24,6 +25,11 @@
}
@Override
+ public Set<String> getMainDexClasses() {
+ throw new Unimplemented();
+ }
+
+ @Override
public String getStdout() {
throw new Unimplemented("Unexpected attempt to access stdout from dx");
}
diff --git a/src/test/java/com/android/tools/r8/ExternalR8TestCompileResult.java b/src/test/java/com/android/tools/r8/ExternalR8TestCompileResult.java
index a7c0baf..cef49f7 100644
--- a/src/test/java/com/android/tools/r8/ExternalR8TestCompileResult.java
+++ b/src/test/java/com/android/tools/r8/ExternalR8TestCompileResult.java
@@ -5,10 +5,12 @@
package com.android.tools.r8;
import com.android.tools.r8.ToolHelper.ProcessResult;
+import com.android.tools.r8.errors.Unimplemented;
import com.android.tools.r8.utils.AndroidApp;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
import java.io.IOException;
import java.nio.file.Path;
+import java.util.Set;
import java.util.concurrent.ExecutionException;
public class ExternalR8TestCompileResult
@@ -51,6 +53,11 @@
}
@Override
+ public Set<String> getMainDexClasses() {
+ throw new Unimplemented();
+ }
+
+ @Override
public String getStdout() {
return processResult.stdout;
}
diff --git a/src/test/java/com/android/tools/r8/Keep.java b/src/test/java/com/android/tools/r8/Keep.java
new file mode 100644
index 0000000..ed8275b
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/Keep.java
@@ -0,0 +1,10 @@
+// Copyright (c) 2018, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+@Retention(RetentionPolicy.CLASS)
+public @interface Keep {}
diff --git a/src/test/java/com/android/tools/r8/ProguardTestBuilder.java b/src/test/java/com/android/tools/r8/ProguardTestBuilder.java
index 0e65de1..7aacf12 100644
--- a/src/test/java/com/android/tools/r8/ProguardTestBuilder.java
+++ b/src/test/java/com/android/tools/r8/ProguardTestBuilder.java
@@ -53,6 +53,11 @@
}
@Override
+ public boolean isProguardTestBuilder() {
+ return true;
+ }
+
+ @Override
ProguardTestBuilder self() {
return this;
}
diff --git a/src/test/java/com/android/tools/r8/ProguardTestCompileResult.java b/src/test/java/com/android/tools/r8/ProguardTestCompileResult.java
index c352417..ca1a0a7 100644
--- a/src/test/java/com/android/tools/r8/ProguardTestCompileResult.java
+++ b/src/test/java/com/android/tools/r8/ProguardTestCompileResult.java
@@ -9,6 +9,7 @@
import com.android.tools.r8.utils.codeinspector.CodeInspector;
import java.io.IOException;
import java.nio.file.Path;
+import java.util.Set;
import java.util.concurrent.ExecutionException;
public class ProguardTestCompileResult
@@ -42,6 +43,11 @@
}
@Override
+ public Set<String> getMainDexClasses() {
+ throw new Unimplemented();
+ }
+
+ @Override
public String getStdout() {
throw new Unimplemented("Unexpected attempt to access stdout from Proguard");
}
diff --git a/src/test/java/com/android/tools/r8/R8RunArtTestsTest.java b/src/test/java/com/android/tools/r8/R8RunArtTestsTest.java
index 4dc03b1..f22a173 100644
--- a/src/test/java/com/android/tools/r8/R8RunArtTestsTest.java
+++ b/src/test/java/com/android/tools/r8/R8RunArtTestsTest.java
@@ -741,8 +741,20 @@
.put(
"095-switch-MAX_INT",
TestCondition.match(
+ TestCondition.tools(DexTool.NONE),
+ TestCondition.compilers(CompilerUnderTest.D8_AFTER_R8CF),
+ TestCondition.runtimes(DexVm.Version.V4_0_4)))
+ .put(
+ "095-switch-MAX_INT",
+ TestCondition.match(
+ TestCondition.tools(DexTool.NONE),
+ TestCondition.compilers(CompilerUnderTest.D8),
+ TestCondition.runtimes(DexVm.Version.V4_0_4)))
+ .put(
+ "095-switch-MAX_INT",
+ TestCondition.match(
TestCondition.tools(DexTool.DX),
- TestCondition.D8_COMPILER,
+ TestCondition.compilers(CompilerUnderTest.D8),
TestCondition.runtimes(DexVm.Version.V4_0_4)))
.build();
diff --git a/src/test/java/com/android/tools/r8/R8RunExamplesAndroidOTest.java b/src/test/java/com/android/tools/r8/R8RunExamplesAndroidOTest.java
index 7157943..389b0fa 100644
--- a/src/test/java/com/android/tools/r8/R8RunExamplesAndroidOTest.java
+++ b/src/test/java/com/android/tools/r8/R8RunExamplesAndroidOTest.java
@@ -107,14 +107,14 @@
.withOptionConsumer(opts -> opts.enableClassInlining = false)
.withBuilderTransformation(
b -> b.addProguardConfiguration(PROGUARD_OPTIONS, Origin.unknown()))
- .withDexCheck(inspector -> checkLambdaCount(inspector, 101, "lambdadesugaring"))
+ .withDexCheck(inspector -> checkLambdaCount(inspector, 102, "lambdadesugaring"))
.run();
test("lambdadesugaring", "lambdadesugaring", "LambdaDesugaring")
.withMinApiLevel(ToolHelper.getMinApiLevelForDexVmNoHigherThan(AndroidApiLevel.K))
.withBuilderTransformation(
b -> b.addProguardConfiguration(PROGUARD_OPTIONS, Origin.unknown()))
- .withDexCheck(inspector -> checkLambdaCount(inspector, 6, "lambdadesugaring"))
+ .withDexCheck(inspector -> checkLambdaCount(inspector, 7, "lambdadesugaring"))
.run();
}
@@ -146,14 +146,14 @@
.withOptionConsumer(opts -> opts.enableClassInlining = false)
.withBuilderTransformation(
b -> b.addProguardConfiguration(PROGUARD_OPTIONS, Origin.unknown()))
- .withDexCheck(inspector -> checkLambdaCount(inspector, 101, "lambdadesugaring"))
+ .withDexCheck(inspector -> checkLambdaCount(inspector, 102, "lambdadesugaring"))
.run();
test("lambdadesugaring", "lambdadesugaring", "LambdaDesugaring")
.withMinApiLevel(AndroidApiLevel.N)
.withBuilderTransformation(
b -> b.addProguardConfiguration(PROGUARD_OPTIONS, Origin.unknown()))
- .withDexCheck(inspector -> checkLambdaCount(inspector, 6, "lambdadesugaring"))
+ .withDexCheck(inspector -> checkLambdaCount(inspector, 7, "lambdadesugaring"))
.run();
}
diff --git a/src/test/java/com/android/tools/r8/R8RunExamplesCommon.java b/src/test/java/com/android/tools/r8/R8RunExamplesCommon.java
index 23ec0c9..8a004a9 100644
--- a/src/test/java/com/android/tools/r8/R8RunExamplesCommon.java
+++ b/src/test/java/com/android/tools/r8/R8RunExamplesCommon.java
@@ -169,6 +169,7 @@
protected void configure(InternalOptions options) {
options.lineNumberOptimization = LineNumberOptimization.OFF;
+ options.testing.allowDexInputForTesting = input == Input.DX;
}
private boolean shouldCompileFail() {
diff --git a/src/test/java/com/android/tools/r8/R8TestBuilder.java b/src/test/java/com/android/tools/r8/R8TestBuilder.java
index e831d72..909d45e 100644
--- a/src/test/java/com/android/tools/r8/R8TestBuilder.java
+++ b/src/test/java/com/android/tools/r8/R8TestBuilder.java
@@ -244,10 +244,6 @@
return self();
}
- public T allowClassInlinerGracefulExit() {
- return addOptionsModification(options -> options.testing.allowClassInlinerGracefulExit = true);
- }
-
/**
* Allow info, warning, and error diagnostics.
*
@@ -314,6 +310,10 @@
return self();
}
+ public T allowUnusedDontWarnPatterns() {
+ return addOptionsModification(options -> options.testing.allowUnusedDontWarnRules = true);
+ }
+
public T allowUnusedProguardConfigurationRules() {
return allowUnusedProguardConfigurationRules(true);
}
diff --git a/src/test/java/com/android/tools/r8/R8TestCompileResult.java b/src/test/java/com/android/tools/r8/R8TestCompileResult.java
index 9b63b86..c9834f8 100644
--- a/src/test/java/com/android/tools/r8/R8TestCompileResult.java
+++ b/src/test/java/com/android/tools/r8/R8TestCompileResult.java
@@ -21,6 +21,7 @@
import java.io.IOException;
import java.nio.file.Path;
import java.util.List;
+import java.util.Set;
import java.util.function.Consumer;
public class R8TestCompileResult extends TestCompileResult<R8TestCompileResult, R8TestRunResult> {
@@ -74,6 +75,11 @@
}
@Override
+ public Set<String> getMainDexClasses() {
+ return state.getMainDexClasses();
+ }
+
+ @Override
public String getStdout() {
return state.getStdout();
}
diff --git a/src/test/java/com/android/tools/r8/RunExamplesAndroidOTest.java b/src/test/java/com/android/tools/r8/RunExamplesAndroidOTest.java
index c72a3ea..b7bb5d2 100644
--- a/src/test/java/com/android/tools/r8/RunExamplesAndroidOTest.java
+++ b/src/test/java/com/android/tools/r8/RunExamplesAndroidOTest.java
@@ -611,14 +611,15 @@
Assume.assumeTrue(ToolHelper.artSupported() || ToolHelper.compareAgaintsGoldenFiles());
boolean expectedToFail = expectedToFail(testName);
try {
- String output = ToolHelper.runArtNoVerificationErrors(
- Arrays.stream(dexes).map(path -> path.toString()).collect(Collectors.toList()),
- qualifiedMainClass,
- builder -> {
- for (String arg : args) {
- builder.appendProgramArgument(arg);
- }
- });
+ String output =
+ ToolHelper.runArtNoVerificationErrors(
+ Arrays.stream(dexes).map(Path::toString).collect(Collectors.toList()),
+ qualifiedMainClass,
+ builder -> {
+ for (String arg : args) {
+ builder.appendProgramArgument(arg);
+ }
+ });
if (!expectedToFail
&& !skipRunningOnJvm(testName)
&& !ToolHelper.compareAgaintsGoldenFiles()) {
diff --git a/src/test/java/com/android/tools/r8/RunExamplesAndroidPTest.java b/src/test/java/com/android/tools/r8/RunExamplesAndroidPTest.java
index d06d976..ed69ecf 100644
--- a/src/test/java/com/android/tools/r8/RunExamplesAndroidPTest.java
+++ b/src/test/java/com/android/tools/r8/RunExamplesAndroidPTest.java
@@ -248,10 +248,11 @@
if (expectedToFail) {
thrown.expect(Throwable.class);
}
- String output = ToolHelper.runArtNoVerificationErrors(
- Arrays.stream(dexes).map(path -> path.toString()).collect(Collectors.toList()),
- qualifiedMainClass,
- null);
+ String output =
+ ToolHelper.runArtNoVerificationErrors(
+ Arrays.stream(dexes).map(Path::toString).collect(Collectors.toList()),
+ qualifiedMainClass,
+ null);
if (!expectedToFail) {
ToolHelper.ProcessResult javaResult =
ToolHelper.runJava(ImmutableList.copyOf(jars), qualifiedMainClass);
diff --git a/src/test/java/com/android/tools/r8/TestBase.java b/src/test/java/com/android/tools/r8/TestBase.java
index e1d4c9e..f6846b4 100644
--- a/src/test/java/com/android/tools/r8/TestBase.java
+++ b/src/test/java/com/android/tools/r8/TestBase.java
@@ -49,7 +49,6 @@
import com.android.tools.r8.shaking.MainDexClasses;
import com.android.tools.r8.shaking.NoStaticClassMergingRule;
import com.android.tools.r8.shaking.NoVerticalClassMergingRule;
-import com.android.tools.r8.shaking.ProguardClassFilter;
import com.android.tools.r8.shaking.ProguardClassNameList;
import com.android.tools.r8.shaking.ProguardConfiguration;
import com.android.tools.r8.shaking.ProguardConfigurationRule;
@@ -720,9 +719,9 @@
MainDexClasses.createEmptyMainDexClasses());
}
- protected static AppView<AppInfoWithClassHierarchy> computeAppViewWithSubtyping(AndroidApp app)
- throws Exception {
- return computeAppViewWithSubtyping(
+ protected static AppView<AppInfoWithClassHierarchy> computeAppViewWithClassHierachy(
+ AndroidApp app) throws Exception {
+ return computeAppViewWithClassHierachy(
app,
factory ->
buildConfigForRules(
@@ -730,7 +729,7 @@
Collections.singletonList(ProguardKeepRule.defaultKeepAllRule(unused -> {}))));
}
- private static AppView<AppInfoWithClassHierarchy> computeAppViewWithSubtyping(
+ private static AppView<AppInfoWithClassHierarchy> computeAppViewWithClassHierachy(
AndroidApp app, Function<DexItemFactory, ProguardConfiguration> keepConfig) throws Exception {
DexItemFactory dexItemFactory = new DexItemFactory();
InternalOptions options = new InternalOptions(keepConfig.apply(dexItemFactory), new Reporter());
@@ -759,7 +758,7 @@
protected static AppView<AppInfoWithLiveness> computeAppViewWithLiveness(
AndroidApp app, Function<DexItemFactory, ProguardConfiguration> keepConfig) throws Exception {
- AppView<AppInfoWithClassHierarchy> appView = computeAppViewWithSubtyping(app, keepConfig);
+ AppView<AppInfoWithClassHierarchy> appView = computeAppViewWithClassHierachy(app, keepConfig);
// Run the tree shaker to compute an instance of AppInfoWithLiveness.
ExecutorService executor = Executors.newSingleThreadExecutor();
SubtypingInfo subtypingInfo = new SubtypingInfo(appView);
@@ -769,8 +768,8 @@
.run(executor);
appView.setRootSet(rootSet);
AppInfoWithLiveness appInfoWithLiveness =
- EnqueuerFactory.createForInitialTreeShaking(appView, subtypingInfo)
- .traceApplication(rootSet, ProguardClassFilter.empty(), executor, Timing.empty());
+ EnqueuerFactory.createForInitialTreeShaking(appView, executor, subtypingInfo)
+ .traceApplication(rootSet, executor, Timing.empty());
// We do not run the tree pruner to ensure that the hierarchy is as designed and not modified
// due to liveness.
return appView.setAppInfo(appInfoWithLiveness);
@@ -1702,6 +1701,10 @@
return AndroidApiLevel.L;
}
+ public static AndroidApiLevel apiLevelWithTwrCloseResourceSupport() {
+ return AndroidApiLevel.K;
+ }
+
public static boolean canUseJavaUtilObjects(TestParameters parameters) {
return parameters.isCfRuntime()
|| parameters.getApiLevel().isGreaterThanOrEqualTo(AndroidApiLevel.K);
diff --git a/src/test/java/com/android/tools/r8/TestBuilderMinAndroidJarTest.java b/src/test/java/com/android/tools/r8/TestBuilderMinAndroidJarTest.java
index 06cba56..d917ab6 100644
--- a/src/test/java/com/android/tools/r8/TestBuilderMinAndroidJarTest.java
+++ b/src/test/java/com/android/tools/r8/TestBuilderMinAndroidJarTest.java
@@ -28,7 +28,10 @@
@Parameters(name = "{0}")
public static TestParametersCollection data() {
- return getTestParameters().withAllRuntimes().withAllApiLevels().build();
+ return getTestParameters()
+ .withAllRuntimes()
+ .withApiLevelsStartingAtIncluding(AndroidApiLevel.N)
+ .build();
}
public TestBuilderMinAndroidJarTest(TestParameters parameters) {
diff --git a/src/test/java/com/android/tools/r8/TestCompileResult.java b/src/test/java/com/android/tools/r8/TestCompileResult.java
index e37e48e..64c7680 100644
--- a/src/test/java/com/android/tools/r8/TestCompileResult.java
+++ b/src/test/java/com/android/tools/r8/TestCompileResult.java
@@ -38,6 +38,7 @@
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
+import java.util.Set;
import java.util.concurrent.ExecutionException;
import java.util.function.Consumer;
import java.util.function.Function;
@@ -90,6 +91,13 @@
return self();
}
+ public abstract Set<String> getMainDexClasses();
+
+ public final CR inspectMainDexClasses(Consumer<Set<String>> consumer) {
+ consumer.accept(getMainDexClasses());
+ return self();
+ }
+
public abstract String getStdout();
public abstract String getStderr();
diff --git a/src/test/java/com/android/tools/r8/TestCompilerBuilder.java b/src/test/java/com/android/tools/r8/TestCompilerBuilder.java
index f196151..79f00e2 100644
--- a/src/test/java/com/android/tools/r8/TestCompilerBuilder.java
+++ b/src/test/java/com/android/tools/r8/TestCompilerBuilder.java
@@ -14,6 +14,7 @@
import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.AndroidApp;
import com.android.tools.r8.utils.AndroidAppConsumers;
+import com.android.tools.r8.utils.DescriptorUtils;
import com.android.tools.r8.utils.ForwardingOutputStream;
import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.ThrowingOutputStream;
@@ -30,10 +31,13 @@
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
+import java.util.Set;
import java.util.concurrent.ExecutionException;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Supplier;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
public abstract class TestCompilerBuilder<
C extends BaseCompilerCommand,
@@ -45,7 +49,9 @@
public static final Consumer<InternalOptions> DEFAULT_OPTIONS =
options -> {
- options.testing.allowClassInlinerGracefulExit = false;
+ options.testing.allowUnusedDontWarnRules = false;
+ options.testing.allowUnnecessaryDontWarnWildcards = false;
+ options.testing.enableExperimentalMissingClassesReporting = true;
options.testing.reportUnusedProguardConfigurationRules = true;
options.horizontalClassMergerOptions().enable();
};
@@ -58,6 +64,7 @@
private boolean useDefaultRuntimeLibrary = true;
private final List<Path> additionalRunClassPath = new ArrayList<>();
private ProgramConsumer programConsumer;
+ private MainDexClassesCollector mainDexClassesCollector;
private StringConsumer mainDexListConsumer;
protected int minApiLevel = ToolHelper.getMinApiLevelForDexVm().getLevel();
private Consumer<InternalOptions> optionsConsumer = DEFAULT_OPTIONS;
@@ -172,7 +179,13 @@
public CR compile() throws CompilationFailedException {
AndroidAppConsumers sink = new AndroidAppConsumers();
builder.setProgramConsumer(sink.wrapProgramConsumer(programConsumer));
- builder.setMainDexListConsumer(mainDexListConsumer);
+ if (mainDexClassesCollector != null || mainDexListConsumer != null) {
+ builder.setMainDexListConsumer(
+ ChainedStringConsumer.builder()
+ .addIfNotNull(mainDexClassesCollector)
+ .addIfNotNull(mainDexListConsumer)
+ .build());
+ }
if (backend.isDex() || !isTestShrinkerBuilder()) {
assert !builder.isMinApiLevelSet()
: "Don't set the API level directly through BaseCompilerCommand.Builder in tests";
@@ -188,6 +201,7 @@
builder.addLibraryFiles(TestBase.runtimeJar(backend));
}
}
+ List<String> mainDexClasses = null;
assertNull(oldStdout);
oldStdout = System.out;
assertNull(oldStderr);
@@ -220,6 +234,9 @@
}
return cr;
} finally {
+ if (mainDexClassesCollector != null) {
+ getState().setMainDexClasses(mainDexClassesCollector.getMainDexClasses());
+ }
if (stdout != null) {
getState().setStdout(stdout.toString());
}
@@ -372,6 +389,12 @@
return self();
}
+ public T collectMainDexClasses() {
+ assert mainDexClassesCollector == null;
+ mainDexClassesCollector = new MainDexClassesCollector();
+ return self();
+ }
+
public T setMainDexListConsumer(StringConsumer consumer) {
assert consumer != null;
this.mainDexListConsumer = consumer;
@@ -467,4 +490,76 @@
builder.addAssertionsConfiguration(assertionsConfigurationGenerator);
return self();
}
+
+ private static class ChainedStringConsumer implements StringConsumer {
+
+ private final List<StringConsumer> consumers;
+
+ ChainedStringConsumer(List<StringConsumer> consumers) {
+ this.consumers = consumers;
+ }
+
+ static Builder builder() {
+ return new Builder();
+ }
+
+ @Override
+ public void accept(String string, DiagnosticsHandler handler) {
+ consumers.forEach(consumer -> consumer.accept(string, handler));
+ }
+
+ @Override
+ public void finished(DiagnosticsHandler handler) {
+ consumers.forEach(consumer -> consumer.finished(handler));
+ }
+
+ static class Builder {
+
+ private final List<StringConsumer> consumers = new ArrayList<>();
+
+ Builder add(StringConsumer consumer) {
+ assert consumer != null;
+ consumers.add(consumer);
+ return this;
+ }
+
+ Builder addIfNotNull(StringConsumer consumer) {
+ return consumer != null ? add(consumer) : this;
+ }
+
+ ChainedStringConsumer build() {
+ return new ChainedStringConsumer(consumers);
+ }
+ }
+ }
+
+ private static class MainDexClassesCollector implements StringConsumer {
+
+ private StringBuilder builder = new StringBuilder();
+ private Set<String> mainDexClasses;
+
+ public Set<String> getMainDexClasses() {
+ assert mainDexClasses != null;
+ return mainDexClasses;
+ }
+
+ @Override
+ public void accept(String string, DiagnosticsHandler handler) {
+ builder.append(string);
+ }
+
+ @Override
+ public void finished(DiagnosticsHandler handler) {
+ mainDexClasses =
+ Stream.of(builder.toString().split(System.lineSeparator()))
+ .map(
+ line -> {
+ assert line.endsWith(".class");
+ return line.substring(0, line.length() - ".class".length());
+ })
+ .map(DescriptorUtils::getJavaTypeFromBinaryName)
+ .collect(Collectors.toSet());
+ builder = null;
+ }
+ }
}
diff --git a/src/test/java/com/android/tools/r8/TestDiagnosticMessages.java b/src/test/java/com/android/tools/r8/TestDiagnosticMessages.java
index 72a22e6..4f8713f 100644
--- a/src/test/java/com/android/tools/r8/TestDiagnosticMessages.java
+++ b/src/test/java/com/android/tools/r8/TestDiagnosticMessages.java
@@ -7,6 +7,7 @@
import static com.android.tools.r8.DiagnosticsMatcher.diagnosticMessage;
import static org.hamcrest.CoreMatchers.not;
+import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
@@ -64,6 +65,11 @@
return assertWarningsMatch(Collections.singletonList(matcher));
}
+ @SuppressWarnings("unchecked")
+ default TestDiagnosticMessages assertWarningsMatch(Matcher<Diagnostic>... matchers) {
+ return assertWarningsMatch(Arrays.asList(matchers));
+ }
+
TestDiagnosticMessages assertWarningsMatch(Collection<Matcher<Diagnostic>> matchers);
default TestDiagnosticMessages assertErrorsMatch(Matcher<Diagnostic> matcher) {
diff --git a/src/test/java/com/android/tools/r8/TestDiagnosticMessagesImpl.java b/src/test/java/com/android/tools/r8/TestDiagnosticMessagesImpl.java
index 3935988..f1b6e73 100644
--- a/src/test/java/com/android/tools/r8/TestDiagnosticMessagesImpl.java
+++ b/src/test/java/com/android/tools/r8/TestDiagnosticMessagesImpl.java
@@ -92,7 +92,7 @@
"Expected no "
+ type
+ " messages, got:\n"
- + String.join("\n", ListUtils.map(messages, m -> m.getDiagnosticMessage())),
+ + String.join("\n", ListUtils.map(messages, Diagnostic::getDiagnosticMessage)),
0,
messages.size());
}
diff --git a/src/test/java/com/android/tools/r8/TestParametersBuilder.java b/src/test/java/com/android/tools/r8/TestParametersBuilder.java
index 9b84425..c5811f9 100644
--- a/src/test/java/com/android/tools/r8/TestParametersBuilder.java
+++ b/src/test/java/com/android/tools/r8/TestParametersBuilder.java
@@ -75,12 +75,12 @@
/** Add all available CF runtimes starting from and including {@param startInclusive}. */
public TestParametersBuilder withCfRuntimesStartingFromIncluding(CfVm startInclusive) {
- return withCfRuntimeFilter(vm -> startInclusive.lessThanOrEqual(vm));
+ return withCfRuntimeFilter(startInclusive::lessThanOrEqual);
}
/** Add all available CF runtimes starting from and excluding {@param startExcluding}. */
public TestParametersBuilder withCfRuntimesStartingFromExcluding(CfVm startExcluding) {
- return withCfRuntimeFilter(vm -> startExcluding.lessThan(vm));
+ return withCfRuntimeFilter(startExcluding::lessThan);
}
/** Add all available CF runtimes ending at and including {@param endInclusive}. */
@@ -115,9 +115,14 @@
return withDexRuntimesStartingFromIncluding(DexVm.Version.V5_1_1);
}
+ /** Add all available DEX runtimes that do not support native multidex. */
+ public TestParametersBuilder withMainDexRuntimes() {
+ return withDexRuntimesEndingAtExcluding(DexVm.Version.V5_1_1);
+ }
+
/** Add all available DEX runtimes starting from and including {@param startInclusive}. */
public TestParametersBuilder withDexRuntimesStartingFromIncluding(DexVm.Version startInclusive) {
- return withDexRuntimeFilter(vm -> startInclusive.isOlderThanOrEqual(vm));
+ return withDexRuntimeFilter(startInclusive::isOlderThanOrEqual);
}
/** Add all available DEX runtimes starting from and excluding {@param startExcluding}. */
diff --git a/src/test/java/com/android/tools/r8/TestParametersCollection.java b/src/test/java/com/android/tools/r8/TestParametersCollection.java
index 52bc8d5..8351a9c 100644
--- a/src/test/java/com/android/tools/r8/TestParametersCollection.java
+++ b/src/test/java/com/android/tools/r8/TestParametersCollection.java
@@ -6,7 +6,6 @@
import java.util.Collection;
import java.util.Iterator;
import java.util.stream.Stream;
-import org.jetbrains.annotations.NotNull;
public class TestParametersCollection implements Iterable<TestParameters> {
@@ -17,7 +16,6 @@
this.parameters = parameters;
}
- @NotNull
@Override
public Iterator<TestParameters> iterator() {
return parameters.iterator();
diff --git a/src/test/java/com/android/tools/r8/TestRunResult.java b/src/test/java/com/android/tools/r8/TestRunResult.java
index c775215..4a7d5c7 100644
--- a/src/test/java/com/android/tools/r8/TestRunResult.java
+++ b/src/test/java/com/android/tools/r8/TestRunResult.java
@@ -66,8 +66,12 @@
}
public RR assertSuccessWithOutputLinesIf(boolean condition, String... expected) {
+ return assertSuccessWithOutputLinesIf(condition, Arrays.asList(expected));
+ }
+
+ public RR assertSuccessWithOutputLinesIf(boolean condition, List<String> expected) {
if (condition) {
- return assertSuccessWithOutputLines(Arrays.asList(expected));
+ return assertSuccessWithOutputLines(expected);
}
return self();
}
diff --git a/src/test/java/com/android/tools/r8/TestShrinkerBuilder.java b/src/test/java/com/android/tools/r8/TestShrinkerBuilder.java
index 1bcbc55..b28a51f 100644
--- a/src/test/java/com/android/tools/r8/TestShrinkerBuilder.java
+++ b/src/test/java/com/android/tools/r8/TestShrinkerBuilder.java
@@ -4,6 +4,8 @@
package com.android.tools.r8;
+import static com.android.tools.r8.ir.desugar.InterfaceMethodRewriter.COMPANION_CLASS_NAME_SUFFIX;
+
import com.android.tools.r8.TestBase.Backend;
import com.android.tools.r8.dexsplitter.SplitterTestBase.RunInterface;
import com.android.tools.r8.references.MethodReference;
@@ -40,6 +42,10 @@
super(state, builder, backend);
}
+ public boolean isProguardTestBuilder() {
+ return false;
+ }
+
@Override
public boolean isTestShrinkerBuilder() {
return true;
@@ -90,7 +96,7 @@
public abstract T addKeepRuleFiles(List<Path> files);
- public T addKeepRuleFiles(Path... files) throws IOException {
+ public T addKeepRuleFiles(Path... files) {
return addKeepRuleFiles(Arrays.asList(files));
}
@@ -101,7 +107,150 @@
}
public T addDontWarn(Class<?> clazz) {
- return addKeepRules("-dontwarn " + clazz.getTypeName());
+ return addDontWarn(clazz.getTypeName());
+ }
+
+ public T addDontWarn(String className) {
+ return addKeepRules("-dontwarn " + className);
+ }
+
+ public T addDontWarn(String... classes) {
+ for (String clazz : classes) {
+ addKeepRules("-dontwarn " + clazz);
+ }
+ return self();
+ }
+
+ @Deprecated
+ public T addDontWarnCompanionClass(Class<?> clazz) {
+ 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.**");
+ }
+
+ public T addDontWarnJavax() {
+ return addDontWarn("javax.**");
+ }
+
+ public T addDontWarnJavaxNullableAnnotation() {
+ return addDontWarn("javax.annotation.Nullable");
+ }
+
+ public T addDontWarnJavaLangInvoke() {
+ return addDontWarn("java.lang.invoke.*");
+ }
+
+ public T addDontWarnJavaLangInvoke(String className) {
+ return addDontWarn("java.lang.invoke." + className);
+ }
+
+ public T addDontWarnJavaNioFile() {
+ return addDontWarn("java.nio.file.**");
+ }
+
+ // TODO(b/176133676): Investigate why there are missing class references to org.jetbrains
+ @Deprecated
+ public T addDontWarnJetBrains() {
+ return addDontWarn("org.jetbrains.**");
+ }
+
+ public T addDontWarnJetBrainsAnnotations() {
+ return addDontWarnJetBrainsNotNullAnnotation().addDontWarnJetBrainsNullableAnnotation();
+ }
+
+ public T addDontWarnJetBrainsNotNullAnnotation() {
+ return addDontWarn("org.jetbrains.annotations.NotNull");
+ }
+
+ public T addDontWarnJetBrainsNullableAnnotation() {
+ return addDontWarn("org.jetbrains.annotations.Nullable");
+ }
+
+ // TODO(b/176133676): Should not report missing classes for Kotlin classes.
+ @Deprecated
+ public T addDontWarnKotlin() {
+ return addDontWarn("kotlin.**");
+ }
+
+ // TODO(b/176133676): Should not report missing classes for Kotlin metadata.
+ @Deprecated
+ public T addDontWarnKotlinMetadata() {
+ 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 addKeepKotlinMetadata() {
@@ -327,6 +476,10 @@
return addTestingAnnotation(NeverInline.class);
}
+ public final T addKeepAnnotation() {
+ return addTestingAnnotation(Keep.class);
+ }
+
public final T addMemberValuePropagationAnnotations() {
return addTestingAnnotation(NeverPropagateValue.class);
}
diff --git a/src/test/java/com/android/tools/r8/TestState.java b/src/test/java/com/android/tools/r8/TestState.java
index 3e28699..a30033b 100644
--- a/src/test/java/com/android/tools/r8/TestState.java
+++ b/src/test/java/com/android/tools/r8/TestState.java
@@ -5,6 +5,7 @@
import java.io.IOException;
import java.nio.file.Path;
+import java.util.Set;
import java.util.function.BiFunction;
import org.junit.rules.TemporaryFolder;
@@ -13,6 +14,8 @@
private final TemporaryFolder temp;
private final TestDiagnosticMessagesImpl messages = new TestDiagnosticMessagesImpl();
+ private Set<String> mainDexClasses;
+
private String stdout;
private String stderr;
@@ -40,6 +43,14 @@
return messages;
}
+ public Set<String> getMainDexClasses() {
+ return mainDexClasses;
+ }
+
+ void setMainDexClasses(Set<String> mainDexClasses) {
+ this.mainDexClasses = mainDexClasses;
+ }
+
public String getStdout() {
return stdout;
}
diff --git a/src/test/java/com/android/tools/r8/ToolHelper.java b/src/test/java/com/android/tools/r8/ToolHelper.java
index a85ae6a..08664a8 100644
--- a/src/test/java/com/android/tools/r8/ToolHelper.java
+++ b/src/test/java/com/android/tools/r8/ToolHelper.java
@@ -510,7 +510,7 @@
}
private static List<File> toFileList(List<String> filePathList) {
- return filePathList.stream().map(path -> new File(path)).collect(Collectors.toList());
+ return filePathList.stream().map(File::new).collect(Collectors.toList());
}
public static class DXCommandBuilder extends CommandBuilder {
@@ -1541,7 +1541,7 @@
assert goldenFileDir.isDirectory();
processResult =
compareAgainstGoldenFiles(
- files.stream().map(f -> new File(f)).collect(Collectors.toList()), goldenFileDir);
+ files.stream().map(File::new).collect(Collectors.toList()), goldenFileDir);
if (processResult.exitCode == 0) {
processResult = readProcessResult(goldenFileDir);
}
@@ -1553,8 +1553,7 @@
if (goldenFilesDirToProp != null) {
File goldenFileDir = new File(goldenFilesDirToProp);
assert goldenFileDir.isDirectory();
- storeAsGoldenFiles(
- files.stream().map(f -> new File(f)).collect(Collectors.toList()), goldenFileDir);
+ storeAsGoldenFiles(files.stream().map(File::new).collect(Collectors.toList()), goldenFileDir);
storeProcessResult(processResult, goldenFileDir);
}
diff --git a/src/test/java/com/android/tools/r8/accessrelaxation/AccessRelaxationTestBase.java b/src/test/java/com/android/tools/r8/accessrelaxation/AccessRelaxationTestBase.java
index ce40a93..1127d82 100644
--- a/src/test/java/com/android/tools/r8/accessrelaxation/AccessRelaxationTestBase.java
+++ b/src/test/java/com/android/tools/r8/accessrelaxation/AccessRelaxationTestBase.java
@@ -23,7 +23,7 @@
this.parameters = parameters;
}
- static void assertPublic(CodeInspector codeInspector, Class clazz, MethodSignature signature) {
+ static void assertPublic(CodeInspector codeInspector, Class<?> clazz, MethodSignature signature) {
ClassSubject classSubject = codeInspector.clazz(clazz);
assertThat(classSubject, isPresent());
MethodSubject methodSubject = classSubject.method(signature);
@@ -31,12 +31,12 @@
assertThat(methodSubject, isPublic());
}
- static void assertNotPublic(CodeInspector codeInspector, Class clazz, MethodSignature signature) {
+ static void assertNotPublic(
+ CodeInspector codeInspector, Class<?> clazz, MethodSignature signature) {
ClassSubject classSubject = codeInspector.clazz(clazz);
assertThat(classSubject, isPresent());
MethodSubject methodSubject = classSubject.method(signature);
assertThat(methodSubject, isPresent());
assertThat(methodSubject, not(isPublic()));
}
-
}
diff --git a/src/test/java/com/android/tools/r8/accessrelaxation/NoRelaxationForSerializableTest.java b/src/test/java/com/android/tools/r8/accessrelaxation/NoRelaxationForSerializableTest.java
index 83a0155..339361b 100644
--- a/src/test/java/com/android/tools/r8/accessrelaxation/NoRelaxationForSerializableTest.java
+++ b/src/test/java/com/android/tools/r8/accessrelaxation/NoRelaxationForSerializableTest.java
@@ -13,6 +13,7 @@
import com.android.tools.r8.R8TestCompileResult;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.naming.MemberNaming.MethodSignature;
+import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.BooleanUtils;
import com.android.tools.r8.utils.FileUtils;
import com.android.tools.r8.utils.StringUtils;
@@ -103,7 +104,11 @@
@Parameterized.Parameters(name = "{0}, access-modification: {1}")
public static List<Object[]> data() {
return buildParameters(
- getTestParameters().withAllRuntimesAndApiLevels().build(), BooleanUtils.values());
+ getTestParameters()
+ .withAllRuntimes()
+ .withApiLevelsStartingAtIncluding(AndroidApiLevel.K)
+ .build(),
+ BooleanUtils.values());
}
public NoRelaxationForSerializableTest(TestParameters parameters, boolean accessModification) {
@@ -141,6 +146,8 @@
R8TestCompileResult result =
testForR8(parameters.getBackend())
.addProgramClasses(CLASSES)
+ .addMemberValuePropagationAnnotations()
+ .addNoVerticalClassMergingAnnotations()
.enableInliningAnnotations()
.addKeepRuleFiles(configuration)
.addKeepRules(KEEPMEMBER_RULES)
@@ -174,6 +181,7 @@
R8TestCompileResult result =
testForR8(parameters.getBackend())
.addProgramClasses(CLASSES)
+ .addNoVerticalClassMergingAnnotations()
.enableInliningAnnotations()
.enableMemberValuePropagationAnnotations()
.addKeepRuleFiles(configuration)
diff --git a/src/test/java/com/android/tools/r8/annotations/SourceDebugExtensionTest.java b/src/test/java/com/android/tools/r8/annotations/SourceDebugExtensionTest.java
index 607f96d..98421ae 100644
--- a/src/test/java/com/android/tools/r8/annotations/SourceDebugExtensionTest.java
+++ b/src/test/java/com/android/tools/r8/annotations/SourceDebugExtensionTest.java
@@ -66,6 +66,7 @@
.addProgramFiles(kotlinSources)
.addKeepAttributes(ProguardKeepAttributes.SOURCE_DEBUG_EXTENSION)
.addKeepAllClassesRule()
+ .addDontWarnJetBrainsNotNullAnnotation()
.setMode(CompilationMode.RELEASE)
.setMinApi(parameters.getApiLevel())
.compile()
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 8bb8e36..902d503 100644
--- a/src/test/java/com/android/tools/r8/cf/KeepDeserializeLambdaMethodTestRunner.java
+++ b/src/test/java/com/android/tools/r8/cf/KeepDeserializeLambdaMethodTestRunner.java
@@ -11,6 +11,7 @@
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.TestShrinkerBuilder;
import com.android.tools.r8.utils.StringUtils;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
import com.android.tools.r8.utils.codeinspector.MethodSubject;
@@ -26,9 +27,9 @@
@RunWith(Parameterized.class)
public class KeepDeserializeLambdaMethodTestRunner extends TestBase {
- private static final Class TEST_CLASS_CF =
+ private static final Class<?> TEST_CLASS_CF =
com.android.tools.r8.cf.KeepDeserializeLambdaMethodTestCf.class;
- private static final Class TEST_CLASS_DEX =
+ private static final Class<?> TEST_CLASS_DEX =
com.android.tools.r8.cf.KeepDeserializeLambdaMethodTestDex.class;
private static final String EXPECTED =
@@ -89,8 +90,10 @@
// TODO(b/148836254): Support deserialized lambdas without the need of additional rules.
"-keep class * { private static synthetic void lambda$*(); }");
} else {
- builder.noMinification();
- builder.noTreeShaking();
+ builder
+ .applyIf(parameters.isDexRuntime(), TestShrinkerBuilder::addDontWarnSerializedLambda)
+ .noMinification()
+ .noTreeShaking();
}
builder
.run(parameters.getRuntime(), getMainClass())
diff --git a/src/test/java/com/android/tools/r8/cf/MissingClassJoinsToObjectTest.java b/src/test/java/com/android/tools/r8/cf/MissingClassJoinsToObjectTest.java
index 81f1d09..43d02cb 100644
--- a/src/test/java/com/android/tools/r8/cf/MissingClassJoinsToObjectTest.java
+++ b/src/test/java/com/android/tools/r8/cf/MissingClassJoinsToObjectTest.java
@@ -51,6 +51,7 @@
.enableInliningAnnotations()
.addProgramClasses(TestClass.class, A.class)
.addKeepMainRule(TestClass.class)
+ .addDontWarn(B.class)
.setMinApi(parameters.getApiLevel())
.compile()
.addRunClasspathFiles(getRuntimeClasspath())
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 497a1bf..ee7ff0f 100644
--- a/src/test/java/com/android/tools/r8/cf/PrintSeedsWithDeserializeLambdaMethodTest.java
+++ b/src/test/java/com/android/tools/r8/cf/PrintSeedsWithDeserializeLambdaMethodTest.java
@@ -9,6 +9,7 @@
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.TestShrinkerBuilder;
import com.android.tools.r8.utils.StringUtils;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
import com.android.tools.r8.utils.codeinspector.MethodSubject;
@@ -53,6 +54,7 @@
.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/cf/SillyBlockOrderTest.java b/src/test/java/com/android/tools/r8/cf/SillyBlockOrderTest.java
index 4484519..d1e4ef7 100644
--- a/src/test/java/com/android/tools/r8/cf/SillyBlockOrderTest.java
+++ b/src/test/java/com/android/tools/r8/cf/SillyBlockOrderTest.java
@@ -10,6 +10,7 @@
import com.android.tools.r8.TestParametersCollection;
import com.android.tools.r8.utils.StringUtils;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import com.android.tools.r8.utils.codeinspector.InstructionSubject;
import com.android.tools.r8.utils.codeinspector.MethodSubject;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -53,7 +54,8 @@
try {
MethodSubject method =
inspector.method(TestClass.class.getMethod("doubleConditional", boolean.class));
- assertEquals(expected, method.streamInstructions().filter(i -> i.isGoto()).count());
+ assertEquals(
+ expected, method.streamInstructions().filter(InstructionSubject::isGoto).count());
} catch (NoSuchMethodException e) {
throw new RuntimeException(e);
}
diff --git a/src/test/java/com/android/tools/r8/cf/UnusedMultiNewArrayTest.java b/src/test/java/com/android/tools/r8/cf/UnusedMultiNewArrayTest.java
index 5b9ad45..0820b25 100644
--- a/src/test/java/com/android/tools/r8/cf/UnusedMultiNewArrayTest.java
+++ b/src/test/java/com/android/tools/r8/cf/UnusedMultiNewArrayTest.java
@@ -20,7 +20,7 @@
@Parameters(name = "{0}")
public static TestParametersCollection data() {
- return getTestParameters().withAllRuntimes().build();
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
}
public UnusedMultiNewArrayTest(TestParameters parameters) {
@@ -32,7 +32,8 @@
testForR8(parameters.getBackend())
.addProgramClasses(TestClass.class)
.addKeepMainRule(TestClass.class)
- .setMinApi(parameters.getRuntime())
+ .addDontWarn(A.class)
+ .setMinApi(parameters.getApiLevel())
.compile();
}
diff --git a/src/test/java/com/android/tools/r8/checkdiscarded/CheckDiscardModifyDiagnosticsLevelTest.java b/src/test/java/com/android/tools/r8/checkdiscarded/CheckDiscardModifyDiagnosticsLevelTest.java
index bac9468..ffe8230 100644
--- a/src/test/java/com/android/tools/r8/checkdiscarded/CheckDiscardModifyDiagnosticsLevelTest.java
+++ b/src/test/java/com/android/tools/r8/checkdiscarded/CheckDiscardModifyDiagnosticsLevelTest.java
@@ -18,6 +18,8 @@
import com.android.tools.r8.checkdiscarded.testclasses.Main;
import com.android.tools.r8.checkdiscarded.testclasses.UnusedClass;
import com.android.tools.r8.checkdiscarded.testclasses.UsedClass;
+import com.android.tools.r8.checkdiscarded.testclasses.WillBeGone;
+import com.android.tools.r8.checkdiscarded.testclasses.WillStay;
import com.android.tools.r8.errors.CheckDiscardDiagnostic;
import com.android.tools.r8.utils.InternalOptions;
import com.google.common.collect.ImmutableList;
@@ -79,7 +81,8 @@
public void dontFailCompilationOnCheckDiscardedFailure() {
try {
testForR8(Backend.DEX)
- .addProgramClasses(UnusedClass.class, UsedClass.class, Main.class)
+ .addProgramClasses(
+ UnusedClass.class, UsedClass.class, Main.class, WillBeGone.class, WillStay.class)
.addKeepMainRule(Main.class)
.addKeepRules("-checkdiscard class " + UsedClass.class.getTypeName())
.addOptionsModification(this::noInlining)
diff --git a/src/test/java/com/android/tools/r8/checkdiscarded/CheckDiscardedTest.java b/src/test/java/com/android/tools/r8/checkdiscarded/CheckDiscardedTest.java
index 61f8917..da2e4cb 100644
--- a/src/test/java/com/android/tools/r8/checkdiscarded/CheckDiscardedTest.java
+++ b/src/test/java/com/android/tools/r8/checkdiscarded/CheckDiscardedTest.java
@@ -46,7 +46,7 @@
}
private void compile(
- Class annotation,
+ Class<?> annotation,
boolean checkMembers,
Consumer<TestDiagnosticMessages> onCompilationFailure) {
R8FullTestBuilder builder = testForR8(Backend.DEX);
@@ -54,7 +54,8 @@
try {
R8TestCompileResult result =
builder
- .addProgramClasses(UnusedClass.class, UsedClass.class, Main.class)
+ .addProgramClasses(
+ UnusedClass.class, UsedClass.class, Main.class, WillBeGone.class, WillStay.class)
.addKeepMainRule(Main.class)
.addKeepRules(checkDiscardRule(checkMembers, annotation))
.minification(minify)
@@ -122,5 +123,4 @@
containsString("is invoked from"))));
compile(WillStay.class, true, check);
}
-
}
diff --git a/src/test/java/com/android/tools/r8/classlookup/LibraryClassExtendsProgramClassTest.java b/src/test/java/com/android/tools/r8/classlookup/LibraryClassExtendsProgramClassTest.java
index 363008d..1b68052 100644
--- a/src/test/java/com/android/tools/r8/classlookup/LibraryClassExtendsProgramClassTest.java
+++ b/src/test/java/com/android/tools/r8/classlookup/LibraryClassExtendsProgramClassTest.java
@@ -195,9 +195,11 @@
public void testWithDontWarn() throws Exception {
testForR8(parameters.getBackend())
.setMinApi(parameters.getApiLevel())
+ .addProgramClasses(TestClass.class)
.addProgramClassFileData(junitClasses)
.addKeepAllClassesRule()
- .addKeepRules("-dontwarn android.test.**")
+ .applyIf(
+ libraryContainsJUnit(), builder -> builder.addKeepRules("-dontwarn android.test.**"))
.addOptionsModification(options -> options.lookupLibraryBeforeProgram = false)
.compile()
.assertNoMessages();
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/AdaptVerticallyMergedResourceFileContentsTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/AdaptVerticallyMergedResourceFileContentsTest.java
index 0ebbb9d..090cb9e 100644
--- a/src/test/java/com/android/tools/r8/classmerging/horizontal/AdaptVerticallyMergedResourceFileContentsTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/AdaptVerticallyMergedResourceFileContentsTest.java
@@ -12,7 +12,6 @@
import com.android.tools.r8.DataEntryResource;
import com.android.tools.r8.NeverClassInline;
-import com.android.tools.r8.NeverInline;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.origin.Origin;
import com.android.tools.r8.utils.DataResourceConsumerForTesting;
@@ -51,9 +50,11 @@
.addHorizontallyMergedClassesInspectorIf(
enableHorizontalClassMerging,
inspector -> inspector.assertMergedInto(B.class, A.class))
+ .addVerticallyMergedClassesInspector(
+ inspector -> inspector.assertMergedIntoSubtype(Parent.class))
.compile()
.run(parameters.getRuntime(), Main.class)
- .assertSuccessWithOutputLines("a", "foo parent", "b")
+ .assertSuccessWithOutputLines("a", "b")
.inspector();
assertThat(codeInspector.clazz(Parent.class), not(isPresent()));
@@ -72,13 +73,7 @@
ImmutableList.of(aClassSubject.getFinalName(), aClassSubject.getFinalName(), newClassName));
}
- @NeverClassInline
- public static class Parent {
- @NeverInline
- public void foo() {
- System.out.println("foo parent");
- }
- }
+ public static class Parent {}
@NeverClassInline
public static class A extends Parent {
@@ -95,13 +90,8 @@
}
public static class Main {
- @NeverInline
- public static void parent(Parent p) {
- p.foo();
- }
-
public static void main(String[] args) {
- parent(new A());
+ new A();
new B();
}
}
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/ClassesWithNativeMethodsTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/ClassesWithNativeMethodsTest.java
index 1f0e3e6..04bb9d7 100644
--- a/src/test/java/com/android/tools/r8/classmerging/horizontal/ClassesWithNativeMethodsTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/ClassesWithNativeMethodsTest.java
@@ -12,6 +12,7 @@
import com.android.tools.r8.NeverClassInline;
import com.android.tools.r8.NeverInline;
import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.utils.codeinspector.HorizontallyMergedClassesInspector;
import org.junit.Test;
public class ClassesWithNativeMethodsTest extends HorizontalClassMergingTestBase {
@@ -31,7 +32,8 @@
.enableInliningAnnotations()
.enableNeverClassInliningAnnotations()
.setMinApi(parameters.getApiLevel())
- .addHorizontallyMergedClassesInspector(inspector -> inspector.assertNoClassesMerged())
+ .addHorizontallyMergedClassesInspector(
+ HorizontallyMergedClassesInspector::assertNoClassesMerged)
.run(parameters.getRuntime(), Main.class)
.assertFailureWithErrorThatMatches(
allOf(
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/ConstructorCantInlineTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/ConstructorCantInlineTest.java
index 935e75e..369ebfd 100644
--- a/src/test/java/com/android/tools/r8/classmerging/horizontal/ConstructorCantInlineTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/ConstructorCantInlineTest.java
@@ -50,7 +50,6 @@
}
}
- @NoHorizontalClassMerging
@NeverClassInline
public static class B extends A {}
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/JavaLambdaMergingTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/JavaLambdaMergingTest.java
index adabb3e..63c1f18 100644
--- a/src/test/java/com/android/tools/r8/classmerging/horizontal/JavaLambdaMergingTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/JavaLambdaMergingTest.java
@@ -4,16 +4,18 @@
package com.android.tools.r8.classmerging.horizontal;
-import static com.android.tools.r8.ir.desugar.LambdaRewriter.LAMBDA_CLASS_NAME_PREFIX;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.references.Reference;
+import com.android.tools.r8.synthesis.SyntheticItemsTestUtils;
import com.android.tools.r8.utils.codeinspector.VerticallyMergedClassesInspector;
import java.util.Set;
import java.util.stream.Collectors;
+import org.junit.Ignore;
import org.junit.Test;
public class JavaLambdaMergingTest extends HorizontalClassMergingTestBase {
@@ -23,6 +25,7 @@
}
@Test
+ @Ignore("b/174809311): Test does not work with hygienic lambdas. Rewrite or remove")
public void test() throws Exception {
testForR8(parameters.getBackend())
.addInnerClasses(getClass())
@@ -38,16 +41,12 @@
inspector -> {
Set<DexType> lambdaSources =
inspector.getSources().stream()
- .filter(x -> x.toSourceString().contains(LAMBDA_CLASS_NAME_PREFIX))
+ .filter(JavaLambdaMergingTest::isLambda)
.collect(Collectors.toSet());
assertEquals(3, lambdaSources.size());
DexType firstTarget = inspector.getTarget(lambdaSources.iterator().next());
for (DexType lambdaSource : lambdaSources) {
- assertTrue(
- inspector
- .getTarget(lambdaSource)
- .toSourceString()
- .contains(LAMBDA_CLASS_NAME_PREFIX));
+ assertTrue(isLambda(inspector.getTarget(lambdaSource)));
assertEquals(firstTarget, inspector.getTarget(lambdaSource));
}
})
@@ -59,6 +58,11 @@
.assertSuccessWithOutputLines("Hello world!");
}
+ private static boolean isLambda(DexType type) {
+ return SyntheticItemsTestUtils.isExternalLambda(
+ Reference.classFromDescriptor(type.toDescriptorString()));
+ }
+
public static class Main {
public static void main(String[] args) {
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/MergingProducesFieldCollisionTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/MergingProducesFieldCollisionTest.java
index 34972f6..13cc54f 100644
--- a/src/test/java/com/android/tools/r8/classmerging/horizontal/MergingProducesFieldCollisionTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/MergingProducesFieldCollisionTest.java
@@ -59,7 +59,6 @@
});
}
- @NoVerticalClassMerging
public static class Parent {
@NeverInline
public void foo() {
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 671f740..ce0425e 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
@@ -35,6 +35,7 @@
}
})
.enableNeverClassInliningAnnotations()
+ .enableNoVerticalClassMergingAnnotations()
.setMinApi(parameters.getApiLevel())
.compile()
.run(parameters.getRuntime(), Main.class)
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/VerticallyMergedClassDistinguishedByCheckCastTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/VerticallyMergedClassDistinguishedByCheckCastTest.java
index 366800a..eba6de8 100644
--- a/src/test/java/com/android/tools/r8/classmerging/horizontal/VerticallyMergedClassDistinguishedByCheckCastTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/VerticallyMergedClassDistinguishedByCheckCastTest.java
@@ -25,6 +25,7 @@
.addOptionsModification(
options ->
options.horizontalClassMergerOptions().enableIf(enableHorizontalClassMerging))
+ .enableInliningAnnotations()
.enableNeverClassInliningAnnotations()
.setMinApi(parameters.getApiLevel())
.run(parameters.getRuntime(), Main.class)
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/VerticallyMergedClassDistinguishedByInstanceOfTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/VerticallyMergedClassDistinguishedByInstanceOfTest.java
index c8ad21d..47994ef 100644
--- a/src/test/java/com/android/tools/r8/classmerging/horizontal/VerticallyMergedClassDistinguishedByInstanceOfTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/VerticallyMergedClassDistinguishedByInstanceOfTest.java
@@ -25,6 +25,7 @@
.addOptionsModification(
options ->
options.horizontalClassMergerOptions().enableIf(enableHorizontalClassMerging))
+ .enableInliningAnnotations()
.enableNeverClassInliningAnnotations()
.setMinApi(parameters.getApiLevel())
.run(parameters.getRuntime(), Main.class)
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/dispatch/OverrideAbstractMethodWithDefaultTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/dispatch/OverrideAbstractMethodWithDefaultTest.java
index e456600..635ec43 100644
--- a/src/test/java/com/android/tools/r8/classmerging/horizontal/dispatch/OverrideAbstractMethodWithDefaultTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/dispatch/OverrideAbstractMethodWithDefaultTest.java
@@ -11,6 +11,7 @@
import com.android.tools.r8.NoVerticalClassMerging;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.classmerging.horizontal.HorizontalClassMergingTestBase;
+import com.android.tools.r8.utils.codeinspector.HorizontallyMergedClassesInspector;
import org.junit.Test;
public class OverrideAbstractMethodWithDefaultTest extends HorizontalClassMergingTestBase {
@@ -31,7 +32,7 @@
.enableNoVerticalClassMergingAnnotations()
.setMinApi(parameters.getApiLevel())
.addHorizontallyMergedClassesInspectorIf(
- enableHorizontalClassMerging, inspector -> inspector.assertNoClassesMerged())
+ enableHorizontalClassMerging, HorizontallyMergedClassesInspector::assertNoClassesMerged)
.run(parameters.getRuntime(), Main.class)
.assertSuccessWithOutputLines("J", "B2")
.inspect(
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/dispatch/OverrideMergeAbsentTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/dispatch/OverrideMergeAbsentTest.java
index 526aa00..9a727f5 100644
--- a/src/test/java/com/android/tools/r8/classmerging/horizontal/dispatch/OverrideMergeAbsentTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/dispatch/OverrideMergeAbsentTest.java
@@ -12,6 +12,7 @@
import com.android.tools.r8.NoVerticalClassMerging;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.classmerging.horizontal.HorizontalClassMergingTestBase;
+import com.android.tools.r8.utils.codeinspector.HorizontallyMergedClassesInspector;
import org.junit.Test;
public class OverrideMergeAbsentTest extends HorizontalClassMergingTestBase {
@@ -32,7 +33,7 @@
.enableNoVerticalClassMergingAnnotations()
.setMinApi(parameters.getApiLevel())
.addHorizontallyMergedClassesInspectorIf(
- enableHorizontalClassMerging, inspector -> inspector.assertNoClassesMerged())
+ enableHorizontalClassMerging, HorizontallyMergedClassesInspector::assertNoClassesMerged)
.run(parameters.getRuntime(), Main.class)
.assertSuccessWithOutputLines("A", "B", "A", "J")
.inspect(
diff --git a/src/test/java/com/android/tools/r8/classmerging/vertical/NoIllegalClassAccessWithAccessModificationsTest.java b/src/test/java/com/android/tools/r8/classmerging/vertical/NoIllegalClassAccessWithAccessModificationsTest.java
index 35e27eb..f2edbce 100644
--- a/src/test/java/com/android/tools/r8/classmerging/vertical/NoIllegalClassAccessWithAccessModificationsTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/vertical/NoIllegalClassAccessWithAccessModificationsTest.java
@@ -42,6 +42,7 @@
.addKeepMainRule(TestClass.class)
.addVerticallyMergedClassesInspector(this::inspectVerticallyMergedClasses)
.allowAccessModification()
+ .enableInliningAnnotations()
.enableNoHorizontalClassMergingAnnotations()
.setMinApi(parameters.getApiLevel())
.compile()
diff --git a/src/test/java/com/android/tools/r8/classmerging/vertical/NonReboundFieldAccessOnMergedClassTest.java b/src/test/java/com/android/tools/r8/classmerging/vertical/NonReboundFieldAccessOnMergedClassTest.java
index 0f28809..c65809a 100644
--- a/src/test/java/com/android/tools/r8/classmerging/vertical/NonReboundFieldAccessOnMergedClassTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/vertical/NonReboundFieldAccessOnMergedClassTest.java
@@ -37,6 +37,7 @@
.addVerticallyMergedClassesInspector(
inspector -> inspector.assertMergedIntoSubtype(C.class))
.enableNeverClassInliningAnnotations()
+ .enableNoVerticalClassMergingAnnotations()
.setMinApi(parameters.getApiLevel())
.compile()
.run(parameters.getRuntime(), Main.class)
diff --git a/src/test/java/com/android/tools/r8/classmerging/vertical/VerticalClassMergerTest.java b/src/test/java/com/android/tools/r8/classmerging/vertical/VerticalClassMergerTest.java
index 8dc4e01..5468cae 100644
--- a/src/test/java/com/android/tools/r8/classmerging/vertical/VerticalClassMergerTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/vertical/VerticalClassMergerTest.java
@@ -39,7 +39,6 @@
import com.android.tools.r8.utils.codeinspector.CodeInspector;
import com.android.tools.r8.utils.codeinspector.FoundClassSubject;
import com.android.tools.r8.utils.codeinspector.MethodSubject;
-import com.google.common.base.Predicates;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import java.io.File;
@@ -336,7 +335,8 @@
CF_DIR.resolve("ConflictingInterfaceSignaturesTest.class"),
CF_DIR.resolve("ConflictingInterfaceSignaturesTest$A.class"),
CF_DIR.resolve("ConflictingInterfaceSignaturesTest$B.class"),
- CF_DIR.resolve("ConflictingInterfaceSignaturesTest$InterfaceImpl.class")
+ CF_DIR.resolve("ConflictingInterfaceSignaturesTest$InterfaceImpl.class"),
+ CF_DIR.resolve("NeverInline.class")
};
Set<String> preservedClassNames =
ImmutableSet.of(
@@ -361,7 +361,8 @@
JAVA8_CF_DIR.resolve("NestedDefaultInterfaceMethodsTest.class"),
JAVA8_CF_DIR.resolve("NestedDefaultInterfaceMethodsTest$A.class"),
JAVA8_CF_DIR.resolve("NestedDefaultInterfaceMethodsTest$B.class"),
- JAVA8_CF_DIR.resolve("NestedDefaultInterfaceMethodsTest$C.class")
+ JAVA8_CF_DIR.resolve("NestedDefaultInterfaceMethodsTest$C.class"),
+ JAVA8_CF_DIR.resolve("NeverInline.class")
};
Set<String> preservedClassNames =
ImmutableSet.of(
@@ -384,7 +385,8 @@
new Path[] {
JAVA8_CF_DIR.resolve("NestedDefaultInterfaceMethodsTest.class"),
JAVA8_CF_DIR.resolve("NestedDefaultInterfaceMethodsTest$A.class"),
- JAVA8_CF_DIR.resolve("NestedDefaultInterfaceMethodsTest$B.class")
+ JAVA8_CF_DIR.resolve("NestedDefaultInterfaceMethodsTest$B.class"),
+ JAVA8_CF_DIR.resolve("NeverInline.class")
};
Set<String> preservedClassNames =
ImmutableSet.of(
@@ -461,8 +463,14 @@
new Path[] {
CF_DIR.resolve("ProguardFieldMapTest.class"),
CF_DIR.resolve("ProguardFieldMapTest$A.class"),
- CF_DIR.resolve("ProguardFieldMapTest$B.class")
+ CF_DIR.resolve("ProguardFieldMapTest$B.class"),
+ CF_DIR.resolve("NeverClassInline.class")
};
+ Set<String> preservedClassNamesWithoutClassMerging =
+ ImmutableSet.of(
+ "classmerging.ProguardFieldMapTest",
+ "classmerging.ProguardFieldMapTest$A",
+ "classmerging.ProguardFieldMapTest$B");
// Try without vertical class merging, to check that output is as expected.
R8TestCompileResult compileResultWithoutClassMerging =
@@ -478,7 +486,7 @@
.allowUnusedProguardConfigurationRules(),
main,
readProgramFiles(programFiles),
- Predicates.alwaysTrue());
+ preservedClassNamesWithoutClassMerging::contains);
ClassNameMapper mappingWithoutClassMerging =
ClassNameMapper.mapperFromString(compileResultWithoutClassMerging.getProguardMap());
@@ -528,8 +536,14 @@
new Path[] {
CF_DIR.resolve("ProguardMethodMapTest.class"),
CF_DIR.resolve("ProguardMethodMapTest$A.class"),
- CF_DIR.resolve("ProguardMethodMapTest$B.class")
+ CF_DIR.resolve("ProguardMethodMapTest$B.class"),
+ CF_DIR.resolve("NeverClassInline.class")
};
+ Set<String> preservedClassNamesWithoutClassMerging =
+ ImmutableSet.of(
+ "classmerging.ProguardMethodMapTest",
+ "classmerging.ProguardMethodMapTest$A",
+ "classmerging.ProguardMethodMapTest$B");
// Try without vertical class merging, to check that output is as expected.
R8TestCompileResult compileResultWithoutClassMerging =
@@ -545,7 +559,7 @@
.allowUnusedProguardConfigurationRules(),
main,
readProgramFiles(programFiles),
- Predicates.alwaysTrue());
+ preservedClassNamesWithoutClassMerging::contains);
ClassNameMapper mappingWithoutClassMerging =
ClassNameMapper.mapperFromString(compileResultWithoutClassMerging.getProguardMap());
@@ -603,8 +617,14 @@
new Path[] {
CF_DIR.resolve("ProguardMethodMapTest.class"),
CF_DIR.resolve("ProguardMethodMapTest$A.class"),
- CF_DIR.resolve("ProguardMethodMapTest$B.class")
+ CF_DIR.resolve("ProguardMethodMapTest$B.class"),
+ CF_DIR.resolve("NeverClassInline.class")
};
+ Set<String> preservedClassNamesWithoutClassMerging =
+ ImmutableSet.of(
+ "classmerging.ProguardMethodMapTest",
+ "classmerging.ProguardMethodMapTest$A",
+ "classmerging.ProguardMethodMapTest$B");
// Try without vertical class merging, to check that output is as expected.
R8TestCompileResult compileResultWithoutClassMerging =
@@ -624,7 +644,7 @@
.allowUnusedProguardConfigurationRules(),
main,
readProgramFiles(programFiles),
- Predicates.alwaysTrue());
+ preservedClassNamesWithoutClassMerging::contains);
ClassNameMapper mappingWithoutClassMerging =
ClassNameMapper.mapperFromString(compileResultWithoutClassMerging.getProguardMap());
@@ -690,7 +710,8 @@
new Path[] {
CF_DIR.resolve("SubClassThatReferencesSuperMethod.class"),
CF_DIR.resolve("SuperClassWithReferencedMethod.class"),
- CF_DIR.resolve("SuperCallRewritingTest.class")
+ CF_DIR.resolve("SuperCallRewritingTest.class"),
+ CF_DIR.resolve("NeverInline.class")
};
Set<String> preservedClassNames =
ImmutableSet.of(
@@ -721,7 +742,8 @@
Path[] programFiles =
new Path[] {
CF_DIR.resolve("SuperClassWithReferencedMethod.class"),
- CF_DIR.resolve("SuperCallRewritingTest.class")
+ CF_DIR.resolve("SuperCallRewritingTest.class"),
+ CF_DIR.resolve("NeverInline.class")
};
// Build SubClassThatReferencesMethod.
@@ -936,7 +958,8 @@
CF_DIR.resolve("ConflictingInterfaceSignaturesTest.class"),
CF_DIR.resolve("ConflictingInterfaceSignaturesTest$A.class"),
CF_DIR.resolve("ConflictingInterfaceSignaturesTest$B.class"),
- CF_DIR.resolve("ConflictingInterfaceSignaturesTest$InterfaceImpl.class")
+ CF_DIR.resolve("ConflictingInterfaceSignaturesTest$InterfaceImpl.class"),
+ CF_DIR.resolve("NeverInline.class")
};
Set<String> preservedClassNames =
ImmutableSet.of(
@@ -958,7 +981,8 @@
new Path[] {
JAVA8_CF_DIR.resolve("MergeDefaultMethodIntoClassTest.class"),
JAVA8_CF_DIR.resolve("MergeDefaultMethodIntoClassTest$A.class"),
- JAVA8_CF_DIR.resolve("MergeDefaultMethodIntoClassTest$B.class")
+ JAVA8_CF_DIR.resolve("MergeDefaultMethodIntoClassTest$B.class"),
+ JAVA8_CF_DIR.resolve("NeverInline.class")
};
ImmutableSet<String> preservedClassNames =
ImmutableSet.of(
@@ -1021,17 +1045,21 @@
Path[] programFiles =
new Path[] {
CF_DIR.resolve("SimpleInterfaceAccessTest.class"),
+ CF_DIR.resolve("SimpleInterfaceAccessTest$1.class"),
CF_DIR.resolve("SimpleInterfaceAccessTest$SimpleInterface.class"),
CF_DIR.resolve("SimpleInterfaceAccessTest$OtherSimpleInterface.class"),
CF_DIR.resolve("SimpleInterfaceAccessTest$OtherSimpleInterfaceImpl.class"),
CF_DIR.resolve("pkg/SimpleInterfaceImplRetriever.class"),
- CF_DIR.resolve("pkg/SimpleInterfaceImplRetriever$SimpleInterfaceImpl.class")
+ CF_DIR.resolve("pkg/SimpleInterfaceImplRetriever$SimpleInterfaceImpl.class"),
+ CF_DIR.resolve("pkg/SimpleInterfaceImplRetriever$1.class"),
+ CF_DIR.resolve("NeverInline.class")
};
// SimpleInterface cannot be merged into SimpleInterfaceImpl because SimpleInterfaceImpl
// is in a different package and is not public.
ImmutableSet<String> preservedClassNames =
ImmutableSet.of(
"classmerging.SimpleInterfaceAccessTest",
+ "classmerging.SimpleInterfaceAccessTest$1",
"classmerging.SimpleInterfaceAccessTest$SimpleInterface",
"classmerging.SimpleInterfaceAccessTest$OtherSimpleInterface",
"classmerging.SimpleInterfaceAccessTest$OtherSimpleInterfaceImpl",
@@ -1081,8 +1109,10 @@
Path[] programFiles =
new Path[] {
CF_DIR.resolve("TemplateMethodTest.class"),
+ CF_DIR.resolve("TemplateMethodTest$1.class"),
CF_DIR.resolve("TemplateMethodTest$AbstractClass.class"),
- CF_DIR.resolve("TemplateMethodTest$AbstractClassImpl.class")
+ CF_DIR.resolve("TemplateMethodTest$AbstractClassImpl.class"),
+ CF_DIR.resolve("NeverInline.class")
};
Set<String> preservedClassNames =
ImmutableSet.of(
diff --git a/src/test/java/com/android/tools/r8/debug/DebugTestBase.java b/src/test/java/com/android/tools/r8/debug/DebugTestBase.java
index 13d59dc..a5a1a8a 100644
--- a/src/test/java/com/android/tools/r8/debug/DebugTestBase.java
+++ b/src/test/java/com/android/tools/r8/debug/DebugTestBase.java
@@ -9,6 +9,7 @@
import com.android.tools.r8.ToolHelper.DexVm;
import com.android.tools.r8.ToolHelper.DexVm.Version;
import com.android.tools.r8.errors.Unreachable;
+import com.android.tools.r8.graph.ClassAccessFlags;
import com.android.tools.r8.naming.ClassNameMapper;
import com.android.tools.r8.naming.ClassNamingForNameMapper;
import com.android.tools.r8.naming.ClassNamingForNameMapper.MappedRange;
@@ -2161,8 +2162,28 @@
}
private static boolean isInLambdaClass(VmMirror mirror, Location location) {
- String classSig = mirror.getClassSignature(location.classID);
- return classSig.contains("$$Lambda$");
+ // TODO(b/174809573): These "lambda" specific methods are highly questionable.
+ // Determine the exact filtering behavior of intellij (which is very likely not name
+ // based) and update this filter accordingly.
+ CommandPacket cmd =
+ new CommandPacket(
+ ReferenceTypeCommandSet.CommandSetID, ReferenceTypeCommandSet.ModifiersCommand);
+ cmd.setNextValueAsReferenceTypeID(location.classID);
+ ReplyPacket reply = mirror.performCommand(cmd);
+ mirror.checkReply(reply);
+ int modifiers = reply.getNextValueAsInt();
+ ClassAccessFlags flags = ClassAccessFlags.fromCfAccessFlags(modifiers);
+ if (!flags.isSynthetic()) {
+ return false;
+ }
+ String signature = mirror.getClassSignature(location.classID);
+ if (signature.contains("-CC")) {
+ // TODO(b/174809573): The need to return false here indicates a questionable test
+ // expectation. Either the test is incorrect or there is a bug in our generation of
+ // -CC classes marked as synthetic as that would lead to unwanted debugger filtering.
+ return false;
+ }
+ return true;
}
private static boolean isLambdaMethod(VmMirror mirror, Location location) {
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 deb97d8..ff841d7 100644
--- a/src/test/java/com/android/tools/r8/debug/KotlinStdLibCompilationTest.java
+++ b/src/test/java/com/android/tools/r8/debug/KotlinStdLibCompilationTest.java
@@ -54,6 +54,7 @@
.noMinification()
.noTreeShaking()
.addKeepAllAttributes()
+ .addDontWarnJetBrainsAnnotations()
.setMode(CompilationMode.DEBUG)
.setMinApi(parameters.getApiLevel())
.compileWithExpectedDiagnostics(TestDiagnosticMessages::assertNoMessages);
diff --git a/src/test/java/com/android/tools/r8/debug/LocalsTest.java b/src/test/java/com/android/tools/r8/debug/LocalsTest.java
index 7eeed7d..68b67a9 100644
--- a/src/test/java/com/android/tools/r8/debug/LocalsTest.java
+++ b/src/test/java/com/android/tools/r8/debug/LocalsTest.java
@@ -285,41 +285,46 @@
"Locals",
breakpoint("Locals", "breakpoint"),
run(),
- inspect(state -> {
- // 1st breakpoint: all lengthOfArray[N] are set to 0
- FrameInspector outerFrame = state.getFrame(1);
+ inspect(
+ state -> {
+ // 1st breakpoint: all lengthOfArray[N] are set to 0
+ FrameInspector outerFrame = state.getFrame(1);
- Map<String, Value> localValues = outerFrame.getLocalValues();
+ Map<String, Value> localValues = outerFrame.getLocalValues();
- for (int i = minIndex; i <= maxIndex; ++i) {
- String varName = "lengthOfArray" + i;
- Assert.assertTrue(localValues.containsKey(varName));
- Assert.assertEquals(Value.createInt(0), localValues.get(varName));
- }
+ for (int i = minIndex; i <= maxIndex; ++i) {
+ String varName = "lengthOfArray" + i;
+ Assert.assertTrue(localValues.containsKey(varName));
+ Assert.assertEquals(Value.createInt(0), localValues.get(varName));
+ }
- // Capture IDs of arrays.
- for (int i = minIndex; i <= maxIndex; ++i) {
- String varName = "array" + i;
- Assert.assertTrue(localValues.containsKey(varName));
- arrayLocals.put(varName, localValues.get(varName));
- }
- }),
+ // Capture IDs of arrays.
+ for (int i = minIndex; i <= maxIndex; ++i) {
+ String varName = "array" + i;
+ Assert.assertTrue(localValues.containsKey(varName));
+ arrayLocals.put(varName, localValues.get(varName));
+ }
+ }),
// Step out to reach next instructions in the tested method
stepOut(),
- inspect(state -> {
- Assert.assertEquals("Locals.java", state.getSourceFile());
- Assert.assertEquals(107, state.getLineNumber());
- // Verify that all arrays have the same value.
- arrayLocals.forEach((name, value) -> state.checkLocal(name, value));
- }),
+ inspect(
+ state -> {
+ Assert.assertEquals("Locals.java", state.getSourceFile());
+ Assert.assertEquals(107, state.getLineNumber());
+ // Verify that all arrays have the same value.
+ arrayLocals.forEach(state::checkLocal);
+ }),
// Step instruction by instruction to ensure all locals previously declared are safe.
- stepUntil(StepKind.OVER, StepLevel.INSTRUCTION, state -> {
- final String sourceFile = state.getSourceFile();
- final int lineNumber = state.getLineNumber();
- arrayLocals.forEach((name, value) -> state.checkLocal(name, value));
- // Stop when we reach the expected line.
- return lineNumber == 125 && sourceFile.equals("Locals.java");
- }),
+ stepUntil(
+ StepKind.OVER,
+ StepLevel.INSTRUCTION,
+ state -> {
+ final String sourceFile = state.getSourceFile();
+ final int lineNumber = state.getLineNumber();
+ arrayLocals.forEach(state::checkLocal);
+ // Stop when we reach the expected line.
+ return lineNumber == 125 && sourceFile.equals("Locals.java");
+ }),
run());
}
diff --git a/src/test/java/com/android/tools/r8/debug/SynchronizedBlockTest.java b/src/test/java/com/android/tools/r8/debug/SynchronizedBlockTest.java
index 0c9c4b1..208c2cb 100644
--- a/src/test/java/com/android/tools/r8/debug/SynchronizedBlockTest.java
+++ b/src/test/java/com/android/tools/r8/debug/SynchronizedBlockTest.java
@@ -28,8 +28,8 @@
public static Collection<Object[]> setup() {
DelayedDebugTestConfig cf =
temp -> new CfDebugTestConfig().addPaths(DebugTestBase.DEBUGGEE_JAR);
- DelayedDebugTestConfig r8cf = temp -> new R8CfDebugTestResourcesConfig(temp);
- DelayedDebugTestConfig d8 = temp -> new D8DebugTestResourcesConfig(temp);
+ DelayedDebugTestConfig r8cf = R8CfDebugTestResourcesConfig::new;
+ DelayedDebugTestConfig d8 = D8DebugTestResourcesConfig::new;
return ImmutableList.of(
new Object[] {"CF", cf},
new Object[] {"D8", d8},
diff --git a/src/test/java/com/android/tools/r8/desugar/DesugarMissingTypeLambdaTest.java b/src/test/java/com/android/tools/r8/desugar/DesugarMissingTypeLambdaTest.java
index 8f304c6..875ead2 100644
--- a/src/test/java/com/android/tools/r8/desugar/DesugarMissingTypeLambdaTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/DesugarMissingTypeLambdaTest.java
@@ -3,8 +3,6 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.desugar;
-import static org.hamcrest.CoreMatchers.containsString;
-import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
@@ -19,6 +17,7 @@
import com.android.tools.r8.errors.InterfaceDesugarMissingTypeDiagnostic;
import com.android.tools.r8.position.Position;
import com.android.tools.r8.references.Reference;
+import com.android.tools.r8.synthesis.SyntheticItemsTestUtils;
import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.StringUtils;
import org.junit.Test;
@@ -79,7 +78,7 @@
assertEquals(
Reference.classFromClass(MissingInterface.class), desugarWarning.getMissingType());
// TODO(b/132671303): The context class should not be the synthesized lambda class.
- assertThat(desugarWarning.getContextType().getDescriptor(), containsString("$$Lambda"));
+ assertTrue(SyntheticItemsTestUtils.isInternalLambda(desugarWarning.getContextType()));
// TODO(b/132671303): The position info should be the method context.
assertEquals(Position.UNKNOWN, desugarWarning.getPosition());
}
diff --git a/src/test/java/com/android/tools/r8/desugar/DesugarToClassFile.java b/src/test/java/com/android/tools/r8/desugar/DesugarToClassFile.java
index c9377fe..a0b196f 100644
--- a/src/test/java/com/android/tools/r8/desugar/DesugarToClassFile.java
+++ b/src/test/java/com/android/tools/r8/desugar/DesugarToClassFile.java
@@ -10,6 +10,7 @@
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import com.android.tools.r8.utils.codeinspector.FoundClassSubject;
import java.nio.file.Path;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -47,8 +48,7 @@
private void checkHasLambdaClass(CodeInspector inspector) {
assertTrue(
- inspector.allClasses().stream()
- .anyMatch(subject -> subject.getOriginalName().contains("-$$Lambda$")));
+ inspector.allClasses().stream().anyMatch(FoundClassSubject::isSynthesizedJavaLambdaClass));
}
@Test
diff --git a/src/test/java/com/android/tools/r8/desugar/InvokeSuperToRewrittenDefaultMethodTest.java b/src/test/java/com/android/tools/r8/desugar/InvokeSuperToRewrittenDefaultMethodTest.java
index f63c4d9..93e46d0 100644
--- a/src/test/java/com/android/tools/r8/desugar/InvokeSuperToRewrittenDefaultMethodTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/InvokeSuperToRewrittenDefaultMethodTest.java
@@ -106,7 +106,6 @@
}
}
- @FunctionalInterface
public interface CharConsumer extends Consumer<Character>, IntConsumer {
void accept(char c);
diff --git a/src/test/java/com/android/tools/r8/desugar/backports/BackportDuplicationTest.java b/src/test/java/com/android/tools/r8/desugar/backports/BackportDuplicationTest.java
index d094c78..acfc021 100644
--- a/src/test/java/com/android/tools/r8/desugar/backports/BackportDuplicationTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/backports/BackportDuplicationTest.java
@@ -3,9 +3,6 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.desugar.backports;
-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.assertTrue;
import static org.junit.Assume.assumeTrue;
@@ -16,10 +13,10 @@
import com.android.tools.r8.TestParametersCollection;
import com.android.tools.r8.desugar.backports.AbstractBackportTest.MiniAssert;
import com.android.tools.r8.references.MethodReference;
-import com.android.tools.r8.synthesis.SyntheticItems;
+import com.android.tools.r8.synthesis.SyntheticItemsTestUtils;
+import com.android.tools.r8.synthesis.SyntheticNaming;
import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.StringUtils;
-import com.android.tools.r8.utils.SyntheticItemsTestUtils;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
import com.android.tools.r8.utils.codeinspector.InstructionSubject;
import com.google.common.collect.ImmutableList;
@@ -200,9 +197,7 @@
private void checkNoOriginalsAndNoInternalSynthetics(CodeInspector inspector) {
inspector.forAllClasses(
clazz -> {
- assertThat(
- clazz.getFinalName(),
- not(containsString(SyntheticItems.INTERNAL_SYNTHETIC_CLASS_SEPARATOR)));
+ SyntheticNaming.verifyNotInternalSynthetic(clazz.getFinalReference());
if (!clazz.getOriginalName().equals(MiniAssert.class.getTypeName())) {
clazz.forAllMethods(
method ->
@@ -233,11 +228,11 @@
// of intermediates.
Set<MethodReference> expectedSynthetics =
ImmutableSet.of(
- SyntheticItemsTestUtils.syntheticMethod(
+ SyntheticItemsTestUtils.syntheticBackportMethod(
User1.class, 0, Character.class.getMethod("compare", char.class, char.class)),
- SyntheticItemsTestUtils.syntheticMethod(
+ SyntheticItemsTestUtils.syntheticBackportMethod(
User1.class, 1, Boolean.class.getMethod("compare", boolean.class, boolean.class)),
- SyntheticItemsTestUtils.syntheticMethod(
+ SyntheticItemsTestUtils.syntheticBackportMethod(
User2.class, 0, Integer.class.getMethod("compare", int.class, int.class)));
assertEquals(expectedSynthetics, getSyntheticMethods(inspector));
}
diff --git a/src/test/java/com/android/tools/r8/desugar/backports/BackportMainDexTest.java b/src/test/java/com/android/tools/r8/desugar/backports/BackportMainDexTest.java
index 45f8957..05f84af 100644
--- a/src/test/java/com/android/tools/r8/desugar/backports/BackportMainDexTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/backports/BackportMainDexTest.java
@@ -3,7 +3,6 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.desugar.backports;
-import static com.android.tools.r8.synthesis.SyntheticItems.EXTERNAL_SYNTHETIC_CLASS_SEPARATOR;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.Assert.assertEquals;
@@ -25,11 +24,11 @@
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.synthesis.SyntheticItemsTestUtils;
import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.AndroidApp;
import com.android.tools.r8.utils.ListUtils;
import com.android.tools.r8.utils.StringUtils;
-import com.android.tools.r8.utils.SyntheticItemsTestUtils;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
@@ -57,11 +56,6 @@
static final List<Class<?>> MAIN_DEX_LIST_CLASSES =
ImmutableList.of(MiniAssert.class, TestClass.class, User2.class);
- static final String SyntheticUnderUser1 =
- User1.class.getTypeName() + EXTERNAL_SYNTHETIC_CLASS_SEPARATOR;
- static final String SyntheticUnderUser2 =
- User2.class.getTypeName() + EXTERNAL_SYNTHETIC_CLASS_SEPARATOR;
-
private final TestParameters parameters;
@Parameterized.Parameters(name = "{0}")
@@ -341,16 +335,16 @@
private ImmutableSet<MethodReference> getNonMainDexExpectedSynthetics()
throws NoSuchMethodException {
return ImmutableSet.of(
- SyntheticItemsTestUtils.syntheticMethod(
+ SyntheticItemsTestUtils.syntheticBackportMethod(
User1.class, 1, Boolean.class.getMethod("compare", boolean.class, boolean.class)));
}
private ImmutableSet<MethodReference> getMainDexExpectedSynthetics()
throws NoSuchMethodException {
return ImmutableSet.of(
- SyntheticItemsTestUtils.syntheticMethod(
+ SyntheticItemsTestUtils.syntheticBackportMethod(
User1.class, 0, Character.class.getMethod("compare", char.class, char.class)),
- SyntheticItemsTestUtils.syntheticMethod(
+ SyntheticItemsTestUtils.syntheticBackportMethod(
User2.class, 0, Integer.class.getMethod("compare", int.class, int.class)));
}
diff --git a/src/test/java/com/android/tools/r8/desugar/bridge/LambdaReturnTypeBridgeTest.java b/src/test/java/com/android/tools/r8/desugar/bridge/LambdaReturnTypeBridgeTest.java
index 2dbcc30..fdb1044 100644
--- a/src/test/java/com/android/tools/r8/desugar/bridge/LambdaReturnTypeBridgeTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/bridge/LambdaReturnTypeBridgeTest.java
@@ -62,12 +62,7 @@
codeInspector -> {
boolean foundBridge = false;
for (FoundClassSubject clazz : codeInspector.allClasses()) {
- if (clazz
- .getOriginalName()
- .contains(
- "-$$Lambda$"
- + LambdaWithMultipleImplementingInterfaces.class.getSimpleName()
- + "$")) {
+ if (clazz.isSynthesizedJavaLambdaClass()) {
// Find bridge method and check whether or not it has a cast.
for (FoundMethodSubject bridge : clazz.allMethods(FoundMethodSubject::isBridge)) {
foundBridge = true;
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 f71c916..2494871 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,6 +174,11 @@
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 cf0d8ef..33d3f35 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
@@ -5,6 +5,7 @@
package com.android.tools.r8.desugar.desugaredlibrary;
import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestShrinkerBuilder;
import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.BooleanUtils;
import com.android.tools.r8.utils.StringUtils;
@@ -15,7 +16,6 @@
import java.util.List;
import java.util.ListIterator;
import java.util.Spliterator;
-import org.jetbrains.annotations.NotNull;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
@@ -64,6 +64,9 @@
.addKeepMainRule(Executor.class)
.setMinApi(parameters.getApiLevel())
.enableCoreLibraryDesugaring(parameters.getApiLevel(), keepRuleConsumer)
+ .applyIf(
+ parameters.getApiLevel().isLessThan(AndroidApiLevel.N),
+ TestShrinkerBuilder::addDontWarnEmulatedLibraryClasses)
.compile()
.addDesugaredCoreLibraryRunClassPath(
this::buildDesugaredLibrary,
@@ -115,21 +118,18 @@
return false;
}
- @NotNull
@Override
public Iterator<E> iterator() {
return null;
}
- @NotNull
@Override
public Object[] toArray() {
return new Object[0];
}
- @NotNull
@Override
- public <T> T[] toArray(@NotNull T[] a) {
+ public <T> T[] toArray(T[] a) {
return null;
}
@@ -144,27 +144,27 @@
}
@Override
- public boolean containsAll(@NotNull Collection<?> c) {
+ public boolean containsAll(Collection<?> c) {
return false;
}
@Override
- public boolean addAll(@NotNull Collection<? extends E> c) {
+ public boolean addAll(Collection<? extends E> c) {
return false;
}
@Override
- public boolean addAll(int index, @NotNull Collection<? extends E> c) {
+ public boolean addAll(int index, Collection<? extends E> c) {
return false;
}
@Override
- public boolean removeAll(@NotNull Collection<?> c) {
+ public boolean removeAll(Collection<?> c) {
return false;
}
@Override
- public boolean retainAll(@NotNull Collection<?> c) {
+ public boolean retainAll(Collection<?> c) {
return false;
}
@@ -199,19 +199,16 @@
return 0;
}
- @NotNull
@Override
public ListIterator<E> listIterator() {
return null;
}
- @NotNull
@Override
public ListIterator<E> listIterator(int index) {
return null;
}
- @NotNull
@Override
public List<E> subList(int fromIndex, int toIndex) {
return null;
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 4bd591e..7206b75 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
@@ -15,7 +15,6 @@
import java.util.Iterator;
import java.util.List;
import java.util.function.Predicate;
-import org.jetbrains.annotations.NotNull;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
@@ -82,6 +81,12 @@
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()
@@ -153,21 +158,18 @@
return false;
}
- @NotNull
@Override
public Iterator<E> iterator() {
return Collections.emptyIterator();
}
- @NotNull
@Override
public Object[] toArray() {
return new Object[0];
}
- @NotNull
@Override
- public <T> T[] toArray(@NotNull T[] a) {
+ public <T> T[] toArray(T[] a) {
return a;
}
@@ -182,22 +184,22 @@
}
@Override
- public boolean containsAll(@NotNull Collection<?> c) {
+ public boolean containsAll(Collection<?> c) {
return false;
}
@Override
- public boolean addAll(@NotNull Collection<? extends E> c) {
+ public boolean addAll(Collection<? extends E> c) {
return false;
}
@Override
- public boolean removeAll(@NotNull Collection<?> c) {
+ public boolean removeAll(Collection<?> c) {
return false;
}
@Override
- public boolean retainAll(@NotNull Collection<?> c) {
+ public boolean retainAll(Collection<?> c) {
return false;
}
@@ -226,21 +228,18 @@
return false;
}
- @NotNull
@Override
public Iterator<E> iterator() {
return Collections.emptyIterator();
}
- @NotNull
@Override
public Object[] toArray() {
return new Object[0];
}
- @NotNull
@Override
- public <T> T[] toArray(@NotNull T[] a) {
+ public <T> T[] toArray(T[] a) {
return a;
}
@@ -255,22 +254,22 @@
}
@Override
- public boolean containsAll(@NotNull Collection<?> c) {
+ public boolean containsAll(Collection<?> c) {
return false;
}
@Override
- public boolean addAll(@NotNull Collection<? extends E> c) {
+ public boolean addAll(Collection<? extends E> c) {
return false;
}
@Override
- public boolean removeAll(@NotNull Collection<?> c) {
+ public boolean removeAll(Collection<?> c) {
return false;
}
@Override
- public boolean retainAll(@NotNull Collection<?> c) {
+ public boolean retainAll(Collection<?> c) {
return false;
}
@@ -299,21 +298,18 @@
return false;
}
- @NotNull
@Override
public Iterator<E> iterator() {
return Collections.emptyIterator();
}
- @NotNull
@Override
public Object[] toArray() {
return new Object[0];
}
- @NotNull
@Override
- public <T> T[] toArray(@NotNull T[] a) {
+ public <T> T[] toArray(T[] a) {
return a;
}
@@ -328,22 +324,22 @@
}
@Override
- public boolean containsAll(@NotNull Collection<?> c) {
+ public boolean containsAll(Collection<?> c) {
return false;
}
@Override
- public boolean addAll(@NotNull Collection<? extends E> c) {
+ public boolean addAll(Collection<? extends E> c) {
return false;
}
@Override
- public boolean removeAll(@NotNull Collection<?> c) {
+ public boolean removeAll(Collection<?> c) {
return false;
}
@Override
- public boolean retainAll(@NotNull Collection<?> c) {
+ public boolean retainAll(Collection<?> c) {
return false;
}
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 0707ab9..ce35ef0 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
@@ -9,6 +9,8 @@
import com.android.tools.r8.D8TestRunResult;
import com.android.tools.r8.R8TestRunResult;
import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestShrinkerBuilder;
+import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.BooleanUtils;
import java.nio.file.Path;
import java.util.ArrayList;
@@ -106,6 +108,9 @@
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 0947a55..9212761 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
@@ -24,8 +24,6 @@
import java.util.List;
import java.util.SortedSet;
import java.util.stream.Stream;
-import org.jetbrains.annotations.NotNull;
-import org.jetbrains.annotations.Nullable;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
@@ -97,10 +95,7 @@
.disableDesugaring()
.compile()
.assertNoMessages()
- .inspect(
- inspector -> {
- this.assertCustomCollectionCallsCorrect(inspector);
- })
+ .inspect(this::assertCustomCollectionCallsCorrect)
.addDesugaredCoreLibraryRunClassPath(
this::buildDesugaredLibrary,
parameters.getApiLevel(),
@@ -138,6 +133,9 @@
.addInnerClasses(CustomCollectionTest.class)
.setMinApi(parameters.getApiLevel())
.addKeepClassAndMembersRules(Executor.class)
+ .applyIf(
+ requiresEmulatedInterfaceCoreLibDesugaring(parameters),
+ builder -> builder.addDontWarnEmulatedLibraryClasses().addDontWarnVivifiedClasses())
.enableCoreLibraryDesugaring(parameters.getApiLevel(), keepRuleConsumer)
.compile()
.inspect(this::assertCustomCollectionCallsCorrect)
@@ -161,7 +159,8 @@
.allMatch(
instr ->
instr.toString().contains("$-EL")
- || instr.toString().contains("Comparator$-CC")));
+ || instr.toString().contains("Comparator$-CC")
+ || instr.toString().contains("Stream$-CC")));
} else {
assertTrue(direct.streamInstructions().noneMatch(instr -> instr.toString().contains("$-EL")));
}
@@ -178,7 +177,8 @@
.allMatch(
instr ->
instr.toString().contains("$-EL")
- || instr.toString().contains("Comparator$-CC")));
+ || instr.toString().contains("Comparator$-CC")
+ || instr.toString().contains("Stream$-CC")));
inherited.streamInstructions().forEach(this::assertInheritedDispatchCorrect);
}
@@ -311,21 +311,18 @@
return false;
}
- @NotNull
@Override
public Iterator<E> iterator() {
return Collections.emptyIterator();
}
- @NotNull
@Override
public Object[] toArray() {
return new Object[0];
}
- @NotNull
@Override
- public <T> T[] toArray(@NotNull T[] a) {
+ public <T> T[] toArray(T[] a) {
return a;
}
@@ -340,22 +337,22 @@
}
@Override
- public boolean containsAll(@NotNull Collection<?> c) {
+ public boolean containsAll(Collection<?> c) {
return false;
}
@Override
- public boolean addAll(@NotNull Collection<? extends E> c) {
+ public boolean addAll(Collection<? extends E> c) {
return false;
}
@Override
- public boolean removeAll(@NotNull Collection<?> c) {
+ public boolean removeAll(Collection<?> c) {
return false;
}
@Override
- public boolean retainAll(@NotNull Collection<?> c) {
+ public boolean retainAll(Collection<?> c) {
return false;
}
@@ -369,25 +366,21 @@
// Implements directly a core library interface which implements other library interfaces.
static class CustomSortedSet<E> implements SortedSet<E> {
- @Nullable
@Override
public Comparator<? super E> comparator() {
return null;
}
- @NotNull
@Override
public SortedSet<E> subSet(E fromElement, E toElement) {
return new CustomSortedSet<>();
}
- @NotNull
@Override
public SortedSet<E> headSet(E toElement) {
return new CustomSortedSet<>();
}
- @NotNull
@Override
public SortedSet<E> tailSet(E fromElement) {
return new CustomSortedSet<>();
@@ -418,21 +411,18 @@
return false;
}
- @NotNull
@Override
public Iterator<E> iterator() {
return Collections.emptyIterator();
}
- @NotNull
@Override
public Object[] toArray() {
return new Object[0];
}
- @NotNull
@Override
- public <T> T[] toArray(@NotNull T[] a) {
+ public <T> T[] toArray(T[] a) {
return a;
}
@@ -447,7 +437,7 @@
}
@Override
- public boolean addAll(@NotNull Collection c) {
+ public boolean addAll(Collection c) {
return false;
}
@@ -455,17 +445,17 @@
public void clear() {}
@Override
- public boolean removeAll(@NotNull Collection c) {
+ public boolean removeAll(Collection c) {
return false;
}
@Override
- public boolean retainAll(@NotNull Collection c) {
+ public boolean retainAll(Collection c) {
return false;
}
@Override
- public boolean containsAll(@NotNull Collection c) {
+ public boolean containsAll(Collection c) {
return false;
}
}
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DesugaredLibraryDeterminismTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DesugaredLibraryDeterminismTest.java
index f7b5e5d..9623e7c 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DesugaredLibraryDeterminismTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DesugaredLibraryDeterminismTest.java
@@ -17,11 +17,16 @@
import com.android.tools.r8.utils.codeinspector.FoundClassSubject;
import com.android.tools.r8.utils.codeinspector.FoundMethodSubject;
import com.android.tools.r8.utils.codeinspector.MethodSubject;
+import com.google.common.collect.Sets;
+import com.google.common.collect.Sets.SetView;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
+import java.util.Collections;
import java.util.IdentityHashMap;
import java.util.Map;
+import java.util.Set;
+import java.util.stream.Collectors;
import org.junit.Assume;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -53,12 +58,33 @@
private void assertIdenticalInspectors(Path libDexFile1, Path libDexFile2) throws IOException {
CodeInspector i1 = new CodeInspector(libDexFile1.resolve("classes.dex"));
CodeInspector i2 = new CodeInspector(libDexFile2.resolve("classes.dex"));
+ assertIdenticalInspectors(i1, i2);
+ }
+
+ public static void assertIdenticalInspectors(CodeInspector i1, CodeInspector i2) {
assertEquals(i1.allClasses().size(), i2.allClasses().size());
Map<DexEncodedMethod, DexEncodedMethod> diffs = new IdentityHashMap<>();
for (FoundClassSubject clazz1 : i1.allClasses()) {
ClassSubject clazz = i2.clazz(clazz1.getOriginalName());
assertTrue(clazz.isPresent());
FoundClassSubject clazz2 = clazz.asFoundClassSubject();
+ Set<String> methods1 =
+ clazz1.allMethods().stream()
+ .map(FoundMethodSubject::toString)
+ .collect(Collectors.toSet());
+ Set<String> methods2 =
+ clazz2.allMethods().stream()
+ .map(FoundMethodSubject::toString)
+ .collect(Collectors.toSet());
+ SetView<String> union = Sets.union(methods1, methods2);
+ assertEquals(
+ "Inspector 1 contains more methods",
+ Collections.emptySet(),
+ Sets.difference(union, methods1));
+ assertEquals(
+ "Inspector 2 contains more methods",
+ Collections.emptySet(),
+ Sets.difference(union, methods2));
assertEquals(clazz1.allMethods().size(), clazz2.allMethods().size());
for (FoundMethodSubject method1 : clazz1.allMethods()) {
MethodSubject method = clazz2.method(method1.asMethodReference());
@@ -79,7 +105,7 @@
assertTrue(printDiffs(diffs), diffs.isEmpty());
}
- private String printDiffs(Map<DexEncodedMethod, DexEncodedMethod> diffs) {
+ private static String printDiffs(Map<DexEncodedMethod, DexEncodedMethod> diffs) {
StringBuilder sb = new StringBuilder();
sb.append("The following methods had differences from one dex file to the other (")
.append(diffs.size())
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 5b87397..8a9fc12 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,7 @@
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.NestBasedAccessDesugaring;
+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;
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 d4f08fc..9447057 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
@@ -11,6 +11,7 @@
import com.android.tools.r8.CompilationFailedException;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestRuntime;
+import com.android.tools.r8.TestShrinkerBuilder;
import com.android.tools.r8.ToolHelper;
import com.android.tools.r8.ToolHelper.ArtCommandBuilder;
import com.android.tools.r8.ToolHelper.ProcessResult;
@@ -247,6 +248,9 @@
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/IterableTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/IterableTest.java
index f998b89..0c16701 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/IterableTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/IterableTest.java
@@ -19,7 +19,6 @@
import java.util.function.Consumer;
import java.util.stream.Stream;
import java.util.stream.StreamSupport;
-import org.jetbrains.annotations.NotNull;
import org.junit.Assume;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -138,7 +137,6 @@
this.collection = collection;
}
- @NotNull
@Override
public Iterator<E> iterator() {
return collection.iterator();
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 31bbcbd..ff7577f 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
@@ -14,6 +14,7 @@
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestCompileResult;
import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestShrinkerBuilder;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.BooleanUtils;
@@ -264,6 +265,9 @@
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 616ec2c..6c443af 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
@@ -6,13 +6,13 @@
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.StringUtils;
import java.util.AbstractCollection;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.function.Predicate;
-import org.jetbrains.annotations.NotNull;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
@@ -46,6 +46,9 @@
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()
@@ -71,7 +74,6 @@
}
static class Col1<E> extends AbstractCollection<E> implements Col1Itf<E> {
- @NotNull
@Override
public Iterator<E> iterator() {
return Collections.emptyIterator();
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/NoDesugaredLibraryDexFileTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/NoDesugaredLibraryDexFileTest.java
index a6ea994..f0e55c0 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/NoDesugaredLibraryDexFileTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/NoDesugaredLibraryDexFileTest.java
@@ -16,8 +16,6 @@
import java.util.Iterator;
import java.util.List;
import java.util.SortedSet;
-import org.jetbrains.annotations.NotNull;
-import org.jetbrains.annotations.Nullable;
import org.junit.Assume;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -97,25 +95,21 @@
// Implements directly a core library interface which implements other library interfaces.
static class CustomSortedSet<E> implements SortedSet<E> {
- @Nullable
@Override
public Comparator<? super E> comparator() {
return null;
}
- @NotNull
@Override
public SortedSet<E> subSet(E fromElement, E toElement) {
return new CustomSortedSet<>();
}
- @NotNull
@Override
public SortedSet<E> headSet(E toElement) {
return new CustomSortedSet<>();
}
- @NotNull
@Override
public SortedSet<E> tailSet(E fromElement) {
return new CustomSortedSet<>();
@@ -146,21 +140,18 @@
return false;
}
- @NotNull
@Override
public Iterator<E> iterator() {
return Collections.emptyIterator();
}
- @NotNull
@Override
public Object[] toArray() {
return new Object[0];
}
- @NotNull
@Override
- public <T> T[] toArray(@NotNull T[] a) {
+ public <T> T[] toArray(T[] a) {
return a;
}
@@ -175,7 +166,7 @@
}
@Override
- public boolean addAll(@NotNull Collection c) {
+ public boolean addAll(Collection c) {
return false;
}
@@ -183,17 +174,17 @@
public void clear() {}
@Override
- public boolean removeAll(@NotNull Collection c) {
+ public boolean removeAll(Collection c) {
return false;
}
@Override
- public boolean retainAll(@NotNull Collection c) {
+ public boolean retainAll(Collection c) {
return false;
}
@Override
- public boolean containsAll(@NotNull Collection c) {
+ public boolean containsAll(Collection c) {
return false;
}
}
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 98ad7dc..da41454 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
@@ -7,6 +7,7 @@
import static org.junit.Assert.assertTrue;
import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.BooleanUtils;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
import java.util.ArrayList;
@@ -17,8 +18,6 @@
import java.util.List;
import java.util.Objects;
import java.util.SortedSet;
-import org.jetbrains.annotations.NotNull;
-import org.jetbrains.annotations.Nullable;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
@@ -67,6 +66,9 @@
.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(
@@ -103,25 +105,21 @@
// Implements directly a core library interface which implements other library interfaces.
static class CustomSortedSet<E> implements SortedSet<E> {
- @Nullable
@Override
public Comparator<? super E> comparator() {
return null;
}
- @NotNull
@Override
public SortedSet<E> subSet(E fromElement, E toElement) {
return new CustomSortedSet<>();
}
- @NotNull
@Override
public SortedSet<E> headSet(E toElement) {
return new CustomSortedSet<>();
}
- @NotNull
@Override
public SortedSet<E> tailSet(E fromElement) {
return new CustomSortedSet<>();
@@ -152,21 +150,18 @@
return false;
}
- @NotNull
@Override
public Iterator<E> iterator() {
return Collections.emptyIterator();
}
- @NotNull
@Override
public Object[] toArray() {
return new Object[0];
}
- @NotNull
@Override
- public <T> T[] toArray(@NotNull T[] a) {
+ public <T> T[] toArray(T[] a) {
return a;
}
@@ -181,7 +176,7 @@
}
@Override
- public boolean addAll(@NotNull Collection c) {
+ public boolean addAll(Collection c) {
return false;
}
@@ -189,17 +184,17 @@
public void clear() {}
@Override
- public boolean removeAll(@NotNull Collection c) {
+ public boolean removeAll(Collection c) {
return false;
}
@Override
- public boolean retainAll(@NotNull Collection c) {
+ public boolean retainAll(Collection c) {
return false;
}
@Override
- public boolean containsAll(@NotNull Collection c) {
+ public boolean containsAll(Collection c) {
return false;
}
}
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/ObjectsTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/ObjectsTest.java
index 8031f43..f62067c 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/ObjectsTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/ObjectsTest.java
@@ -421,8 +421,8 @@
private String expectedOutput(boolean objectsRequireNonNullWithSupplierSupported) {
return StringUtils.lines(
"1",
- "true",
- "true",
+ "false",
+ "false",
Objects.toString(Objects.hash(1, 2)),
"4",
"NPE",
@@ -525,8 +525,8 @@
boolean objectsRequireNonNullWithSupplierSupported = Boolean.parseBoolean(args[0]);
// Android K methods.
objectsCompare("b", "a");
- objectsDeepEquals(args, args);
- objectsEquals(args, args);
+ objectsDeepEquals(args, new Object());
+ objectsEquals(args, new Object());
objectsHash(1, 2);
objectsHashCode(4);
objectsRequireNonNull(System.currentTimeMillis() >= 0 ? null : new Object());
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 69f366f..07f4415 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
@@ -14,6 +14,7 @@
import com.android.tools.r8.D8TestRunResult;
import com.android.tools.r8.R8TestRunResult;
import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestShrinkerBuilder;
import com.android.tools.r8.ToolHelper;
import com.android.tools.r8.ToolHelper.DexVm;
import com.android.tools.r8.utils.AndroidApiLevel;
@@ -120,6 +121,9 @@
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 59dbd1b..3eb81ed 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
@@ -5,7 +5,6 @@
package com.android.tools.r8.desugar.desugaredlibrary;
import com.android.tools.r8.TestParameters;
-import com.android.tools.r8.ToolHelper.DexVm.Version;
import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.BooleanUtils;
import java.time.Instant;
@@ -64,6 +63,13 @@
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 2ad9b4f..9496b9c4 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
@@ -6,7 +6,9 @@
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestRuntime.CfVm;
+import com.android.tools.r8.TestShrinkerBuilder;
import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.BooleanUtils;
import com.android.tools.r8.utils.StringUtils;
import java.nio.file.Path;
@@ -79,6 +81,9 @@
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 cb8c24f..42997cb 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,6 +172,9 @@
.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)
@@ -189,6 +192,7 @@
.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 5c01507..1298096 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
@@ -23,7 +23,6 @@
import java.util.List;
import java.util.function.Consumer;
import java.util.stream.Collectors;
-import org.jetbrains.annotations.NotNull;
import org.junit.BeforeClass;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -155,6 +154,10 @@
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)
@@ -224,15 +227,13 @@
return Collections.<E>singletonList(null).iterator();
}
- @NotNull
@Override
public Object[] toArray() {
return new Object[0];
}
- @NotNull
@Override
- public <T> T[] toArray(@NotNull T[] a) {
+ public <T> T[] toArray(T[] a) {
return null;
}
@@ -247,22 +248,22 @@
}
@Override
- public boolean containsAll(@NotNull Collection<?> c) {
+ public boolean containsAll(Collection<?> c) {
return false;
}
@Override
- public boolean addAll(@NotNull Collection<? extends E> c) {
+ public boolean addAll(Collection<? extends E> c) {
return false;
}
@Override
- public boolean removeAll(@NotNull Collection<?> c) {
+ public boolean removeAll(Collection<?> c) {
return false;
}
@Override
- public boolean retainAll(@NotNull Collection<?> c) {
+ public boolean retainAll(Collection<?> c) {
return false;
}
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 b62045b..8fa7780 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,6 +84,9 @@
.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/conversiontests/FunctionConversionTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/FunctionConversionTest.java
index 6afc3a5..24588c6 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/FunctionConversionTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/FunctionConversionTest.java
@@ -174,7 +174,7 @@
private Object1 field;
- private Object2(Object1 o) {
+ Object2(Object1 o) {
this.field = o;
}
}
@@ -183,7 +183,7 @@
private Object2 field;
- private Object3(Object2 o) {
+ Object3(Object2 o) {
this.field = o;
}
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 249f26f..0e66723 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
@@ -9,6 +9,7 @@
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.desugar.desugaredlibrary.DesugaredLibraryTestBase;
+import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.BooleanUtils;
import dalvik.system.PathClassLoader;
import java.sql.SQLDataException;
@@ -24,8 +25,6 @@
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
-import org.jetbrains.annotations.NotNull;
-import org.jetbrains.annotations.Nullable;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
@@ -75,6 +74,11 @@
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)
@@ -301,7 +305,6 @@
return null;
}
- @Nullable
@Override
public V put(K k, V v) {
return null;
@@ -313,46 +316,43 @@
}
@Override
- public void putAll(@NotNull Map<? extends K, ? extends V> map) {}
+ public void putAll(Map<? extends K, ? extends V> map) {}
@Override
public void clear() {}
- @NotNull
@Override
public Set<K> keySet() {
return null;
}
- @NotNull
@Override
public Collection<V> values() {
return null;
}
- @NotNull
@Override
public Set<Entry<K, V>> entrySet() {
return null;
}
@Override
- public V putIfAbsent(@NotNull K k, V v) {
+ public V putIfAbsent(K k, V v) {
return null;
}
@Override
- public boolean remove(@NotNull Object o, Object o1) {
+ public boolean remove(Object o, Object o1) {
return false;
}
@Override
- public boolean replace(@NotNull K k, @NotNull V v, @NotNull V v1) {
+ public boolean replace(K k, V v, V v1) {
return false;
}
@Override
- public V replace(@NotNull K k, @NotNull V v) {
+ public V replace(K k, V v) {
return null;
}
}
@@ -394,7 +394,6 @@
return null;
}
- @Nullable
@Override
public V put(K k, V v) {
return null;
@@ -406,46 +405,43 @@
}
@Override
- public void putAll(@NotNull Map<? extends K, ? extends V> map) {}
+ public void putAll(Map<? extends K, ? extends V> map) {}
@Override
public void clear() {}
- @NotNull
@Override
public Set<K> keySet() {
return null;
}
- @NotNull
@Override
public Collection<V> values() {
return null;
}
- @NotNull
@Override
public Set<Entry<K, V>> entrySet() {
return null;
}
@Override
- public V putIfAbsent(@NotNull K k, V v) {
+ public V putIfAbsent(K k, V v) {
return null;
}
@Override
- public boolean remove(@NotNull Object o, Object o1) {
+ public boolean remove(Object o, Object o1) {
return false;
}
@Override
- public boolean replace(@NotNull K k, @NotNull V v, @NotNull V v1) {
+ public boolean replace(K k, V v, V v1) {
return false;
}
@Override
- public V replace(@NotNull K k, @NotNull V v) {
+ public V replace(K k, V v) {
return null;
}
@@ -475,21 +471,18 @@
return false;
}
- @NotNull
@Override
public Iterator<E> iterator() {
return null;
}
- @NotNull
@Override
public Object[] toArray() {
return new Object[0];
}
- @NotNull
@Override
- public <T> T[] toArray(@NotNull T[] ts) {
+ public <T> T[] toArray(T[] ts) {
return null;
}
@@ -504,27 +497,27 @@
}
@Override
- public boolean containsAll(@NotNull Collection<?> collection) {
+ public boolean containsAll(Collection<?> collection) {
return false;
}
@Override
- public boolean addAll(@NotNull Collection<? extends E> collection) {
+ public boolean addAll(Collection<? extends E> collection) {
return false;
}
@Override
- public boolean addAll(int i, @NotNull Collection<? extends E> collection) {
+ public boolean addAll(int i, Collection<? extends E> collection) {
return false;
}
@Override
- public boolean removeAll(@NotNull Collection<?> collection) {
+ public boolean removeAll(Collection<?> collection) {
return false;
}
@Override
- public boolean retainAll(@NotNull Collection<?> collection) {
+ public boolean retainAll(Collection<?> collection) {
return false;
}
@@ -559,19 +552,16 @@
return 0;
}
- @NotNull
@Override
public ListIterator<E> listIterator() {
return null;
}
- @NotNull
@Override
public ListIterator<E> listIterator(int i) {
return null;
}
- @NotNull
@Override
public List<E> subList(int i, int i1) {
return null;
@@ -603,21 +593,18 @@
return false;
}
- @NotNull
@Override
public Iterator<E> iterator() {
return null;
}
- @NotNull
@Override
public Object[] toArray() {
return new Object[0];
}
- @NotNull
@Override
- public <T> T[] toArray(@NotNull T[] ts) {
+ public <T> T[] toArray(T[] ts) {
return null;
}
@@ -632,27 +619,27 @@
}
@Override
- public boolean containsAll(@NotNull Collection<?> collection) {
+ public boolean containsAll(Collection<?> collection) {
return false;
}
@Override
- public boolean addAll(@NotNull Collection<? extends E> collection) {
+ public boolean addAll(Collection<? extends E> collection) {
return false;
}
@Override
- public boolean addAll(int i, @NotNull Collection<? extends E> collection) {
+ public boolean addAll(int i, Collection<? extends E> collection) {
return false;
}
@Override
- public boolean removeAll(@NotNull Collection<?> collection) {
+ public boolean removeAll(Collection<?> collection) {
return false;
}
@Override
- public boolean retainAll(@NotNull Collection<?> collection) {
+ public boolean retainAll(Collection<?> collection) {
return false;
}
@@ -687,19 +674,16 @@
return 0;
}
- @NotNull
@Override
public ListIterator<E> listIterator() {
return null;
}
- @NotNull
@Override
public ListIterator<E> listIterator(int i) {
return null;
}
- @NotNull
@Override
public List<E> subList(int i, int i1) {
return null;
@@ -710,7 +694,6 @@
}
static class CollectionMapImplements2<R, C> implements Iterable<R>, Map<R, C> {
- @NotNull
@Override
public Iterator<R> iterator() {
return null;
@@ -741,7 +724,6 @@
return null;
}
- @Nullable
@Override
public C put(R r, C c) {
return null;
@@ -753,24 +735,21 @@
}
@Override
- public void putAll(@NotNull Map<? extends R, ? extends C> map) {}
+ public void putAll(Map<? extends R, ? extends C> map) {}
@Override
public void clear() {}
- @NotNull
@Override
public Set<R> keySet() {
return null;
}
- @NotNull
@Override
public Collection<C> values() {
return null;
}
- @NotNull
@Override
public Set<Entry<R, C>> entrySet() {
return null;
@@ -778,7 +757,6 @@
}
static class CollectionMapExtendImplement<R, C> extends HashMap<R, C> implements Iterable<R> {
- @NotNull
@Override
public Iterator<R> iterator() {
return null;
@@ -809,7 +787,6 @@
return null;
}
- @Nullable
@Override
public C put(R r, C c) {
return null;
@@ -821,24 +798,21 @@
}
@Override
- public void putAll(@NotNull Map<? extends R, ? extends C> map) {}
+ public void putAll(Map<? extends R, ? extends C> map) {}
@Override
public void clear() {}
- @NotNull
@Override
public Set<R> keySet() {
return null;
}
- @NotNull
@Override
public Collection<C> values() {
return null;
}
- @NotNull
@Override
public Set<Entry<R, C>> entrySet() {
return null;
@@ -847,7 +821,6 @@
static class CollectionMapImplements2Integer1<C>
implements Iterable<PathClassLoader>, Map<PathClassLoader, C> {
- @NotNull
@Override
public Iterator<PathClassLoader> iterator() {
return null;
@@ -878,7 +851,6 @@
return null;
}
- @Nullable
@Override
public C put(PathClassLoader unsafe, C c) {
return null;
@@ -890,24 +862,21 @@
}
@Override
- public void putAll(@NotNull Map<? extends PathClassLoader, ? extends C> map) {}
+ public void putAll(Map<? extends PathClassLoader, ? extends C> map) {}
@Override
public void clear() {}
- @NotNull
@Override
public Set<PathClassLoader> keySet() {
return null;
}
- @NotNull
@Override
public Collection<C> values() {
return null;
}
- @NotNull
@Override
public Set<Entry<PathClassLoader, C>> entrySet() {
return null;
@@ -916,7 +885,6 @@
static class CollectionMapExtendImplementInteger1<C> extends HashMap<PathClassLoader, C>
implements Iterable<PathClassLoader> {
- @NotNull
@Override
public Iterator<PathClassLoader> iterator() {
return null;
@@ -924,7 +892,6 @@
}
static class CollectionMapImplements2Integer2<R> implements Iterable<R>, Map<R, PathClassLoader> {
- @NotNull
@Override
public Iterator<R> iterator() {
return null;
@@ -955,7 +922,6 @@
return null;
}
- @Nullable
@Override
public PathClassLoader put(R r, PathClassLoader unsafe) {
return null;
@@ -967,24 +933,21 @@
}
@Override
- public void putAll(@NotNull Map<? extends R, ? extends PathClassLoader> map) {}
+ public void putAll(Map<? extends R, ? extends PathClassLoader> map) {}
@Override
public void clear() {}
- @NotNull
@Override
public Set<R> keySet() {
return null;
}
- @NotNull
@Override
public Collection<PathClassLoader> values() {
return null;
}
- @NotNull
@Override
public Set<Entry<R, PathClassLoader>> entrySet() {
return null;
@@ -993,7 +956,6 @@
static class CollectionMapExtendImplementInteger2<R> extends HashMap<R, PathClassLoader>
implements Iterable<R> {
- @NotNull
@Override
public Iterator<R> iterator() {
return null;
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/gson/GsonAllMapsTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/gson/GsonAllMapsTest.java
index 45e6af7..d5a6637 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/gson/GsonAllMapsTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/gson/GsonAllMapsTest.java
@@ -61,6 +61,7 @@
.addProgramFiles(GSON_2_8_1_JAR)
.addKeepMainRule(AllMapsTestClass.class)
.addKeepRuleFiles(GSON_CONFIGURATION)
+ .allowUnusedDontWarnPatterns()
.allowUnusedProguardConfigurationRules()
.allowDiagnosticMessages()
.enableCoreLibraryDesugaring(parameters.getApiLevel(), keepRuleConsumer)
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/gson/GsonOptionalTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/gson/GsonOptionalTest.java
index d3fd68a..62cf8d2 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/gson/GsonOptionalTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/gson/GsonOptionalTest.java
@@ -56,6 +56,7 @@
.addProgramFiles(GSON_2_8_1_JAR)
.addKeepMainRule(OptionalTestClass.class)
.addKeepRuleFiles(GSON_CONFIGURATION)
+ .allowUnusedDontWarnPatterns()
.allowUnusedProguardConfigurationRules()
.addOptionsModification(opt -> opt.ignoreMissingClasses = true)
.allowDiagnosticMessages()
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/jdktests/Jdk11MathTests.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/jdktests/Jdk11MathTests.java
index 8a07522..2c62b8f 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/jdktests/Jdk11MathTests.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/jdktests/Jdk11MathTests.java
@@ -113,6 +113,7 @@
.addKeepMainRule(DIVMOD)
.addKeepMainRule(EXACTARITH)
.addProgramFiles(JDK_11_MATH_TEST_CLASS_FILES)
+ .addLibraryFiles(ToolHelper.getMostRecentAndroidJar())
.setMinApi(parameters.getRuntime())
.run(parameters.getRuntime(), EXACTARITH)
.assertSuccessWithOutput("");
@@ -124,6 +125,7 @@
.addKeepMainRule(DIVMOD)
.addKeepMainRule(EXACTARITH)
.addProgramFiles(JDK_11_MATH_TEST_CLASS_FILES)
+ .addLibraryFiles(ToolHelper.getMostRecentAndroidJar())
.setMinApi(parameters.getRuntime())
.run(parameters.getRuntime(), DIVMOD)
.assertSuccessWithOutput("");
@@ -134,6 +136,7 @@
testForR8(parameters.getBackend())
.addProgramFiles(JDK_11_STRICT_MATH_TEST_CLASS_FILES)
.addKeepMainRule(EXACTARITH)
+ .addLibraryFiles(ToolHelper.getMostRecentAndroidJar())
.setMinApi(parameters.getRuntime())
.run(parameters.getRuntime(), EXACTARITH)
.assertSuccessWithOutput("");
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/jdktests/Jdk11StreamTests.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/jdktests/Jdk11StreamTests.java
index 394f36c..0901c28 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/jdktests/Jdk11StreamTests.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/jdktests/Jdk11StreamTests.java
@@ -287,14 +287,17 @@
D8TestRunResult result =
compileResult.run(
parameters.getRuntime(), "TestNGMainRunner", verbosity, runnableTests.get(path));
- if (result.getStdOut().contains("java.lang.NoSuchMethodError")
- && Arrays.stream(missingDesugaredMethods())
- .anyMatch(method -> result.getStdOut().contains(method))) {
+ String stdout = result.getStdOut();
+ if (stdout.contains("java.lang.NoSuchMethodError")
+ && Arrays.stream(missingDesugaredMethods()).anyMatch(stdout::contains)) {
// TODO(b/134732760): support Java 9 APIs.
- } else if (result.getStdOut().contains("in class Ljava/util/Random")
- && result.getStdOut().contains("java.lang.NoSuchMethodError")) {
+ } else if (stdout.contains("java.lang.NoSuchMethodError")
+ && stdout.contains("org.openjdk.tests.java.util.stream.IterateTest.testIterate")) {
+ // TODO(b/134732760): support Java 9 APIs.
+ } else if (stdout.contains("in class Ljava/util/Random")
+ && stdout.contains("java.lang.NoSuchMethodError")) {
// TODO(b/134732760): Random Java 9 Apis, support or do not use them.
- } else if (result.getStdOut().contains("java.lang.AssertionError")) {
+ } else if (stdout.contains("java.lang.AssertionError")) {
// TODO(b/134732760): Investigate and fix these issues.
} else {
String errorMessage = "STDOUT:\n" + result.getStdOut() + "STDERR:\n" + result.getStdErr();
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 89748a7..aa5393a 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
@@ -133,7 +133,8 @@
.addKeepAllClassesRule()
.addKeepAttributes(ProguardKeepAttributes.RUNTIME_VISIBLE_ANNOTATIONS)
.setMinApi(parameters.getApiLevel())
- .allowDiagnosticWarningMessages();
+ .allowDiagnosticWarningMessages()
+ .addDontWarnJetBrains();
KeepRuleConsumer keepRuleConsumer = null;
if (desugarLibrary) {
keepRuleConsumer = createKeepRuleConsumer(parameters);
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/r8ondex/R8CompiledThroughDexTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/r8ondex/R8CompiledThroughDexTest.java
index b61e9d8..bb66f2e 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/r8ondex/R8CompiledThroughDexTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/r8ondex/R8CompiledThroughDexTest.java
@@ -7,7 +7,11 @@
import static junit.framework.TestCase.assertEquals;
import static org.junit.Assert.assertTrue;
+import com.android.tools.r8.CompilationMode;
+import com.android.tools.r8.OutputMode;
import com.android.tools.r8.R8;
+import com.android.tools.r8.R8Command;
+import com.android.tools.r8.R8Command.Builder;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
import com.android.tools.r8.TestRuntime;
@@ -16,10 +20,15 @@
import com.android.tools.r8.ToolHelper.ProcessResult;
import com.android.tools.r8.cf.bootstrap.BootstrapCurrentEqualityTest;
import com.android.tools.r8.desugar.desugaredlibrary.DesugaredLibraryTestBase;
-import java.io.File;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.Pair;
+import com.google.common.collect.ImmutableList;
import java.nio.file.Path;
import java.nio.file.Paths;
+import java.util.ArrayList;
import java.util.Collections;
+import java.util.List;
+import java.util.function.Consumer;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
@@ -29,18 +38,16 @@
@RunWith(Parameterized.class)
public class R8CompiledThroughDexTest extends DesugaredLibraryTestBase {
- private static final boolean testExternal = true;
+ private static final boolean minify = false;
private final TestParameters parameters;
@Parameters(name = "{0}")
public static TestParametersCollection data() {
- // We do not use withDexRuntimesStartingAtIncluding to exclude dex-default and therefore
- // avoid this 2 * 8 minutes test running on tools/test.py.
+ // We only run this test with ART 8 and with full desugaring to avoid the large runtime on ART.
return getTestParameters()
.withDexRuntime(Version.V8_1_0)
- .withDexRuntime(Version.V9_0_0)
- .withAllApiLevels()
+ .withApiLevel(AndroidApiLevel.B)
.build();
}
@@ -49,13 +56,61 @@
}
private static String commandLinePathFor(String string) {
+ return commandLinePathFor(Paths.get(string));
+ }
+
+ private static String commandLinePathFor(Path path) {
// We switch to absolute path due to the art frameworks requiring to run the command in a
// different folder.
- return Paths.get(string).toAbsolutePath().toString();
+ return path.toAbsolutePath().toString();
}
private static final String R8_KEEP = Paths.get("src/main/keep.txt").toAbsolutePath().toString();
+ private Pair<List<String>, Consumer<Builder>> buildArguments() {
+ ImmutableList.Builder<String> arguments = ImmutableList.builder();
+ List<Consumer<Builder>> buildup = new ArrayList<>();
+
+ arguments.add(commandLinePathFor(ToolHelper.R8_WITH_RELOCATED_DEPS_JAR));
+ buildup.add(b -> b.addProgramFiles(ToolHelper.R8_WITH_RELOCATED_DEPS_JAR));
+
+ arguments.add("--release");
+ buildup.add(b -> b.setMode(CompilationMode.RELEASE));
+
+ arguments.add("--min-api").add(Integer.toString(parameters.getApiLevel().getLevel()));
+ buildup.add(b -> b.setMinApiLevel(parameters.getApiLevel().getLevel()));
+
+ arguments.add("--lib").add(commandLinePathFor(ToolHelper.JAVA_8_RUNTIME));
+ buildup.add(b -> b.addLibraryFiles(ToolHelper.getJava8RuntimeJar()));
+
+ arguments.add("--pg-conf").add(commandLinePathFor(R8_KEEP));
+ buildup.add(b -> b.addProguardConfigurationFiles(Paths.get(R8_KEEP)));
+
+ if (!minify) {
+ arguments.add("--no-minification");
+ buildup.add(b -> b.setDisableMinification(true));
+ }
+
+ Consumer<Builder> consumer = b -> {};
+ for (Consumer<Builder> step : buildup) {
+ consumer = consumer.andThen(step);
+ }
+
+ return new Pair<>(arguments.build(), consumer);
+ }
+
+ private List<String> getSharedArguments() {
+ return buildArguments().getFirst();
+ }
+
+ private Consumer<Builder> getSharedBuilder() {
+ return buildArguments().getSecond();
+ }
+
+ private void printTime(String title, long start) {
+ System.out.println(title + ": " + ((System.nanoTime() - start) / 1000000000) + "s");
+ }
+
@Test
public void testR8CompiledWithR8Dex() throws Exception {
// Compile once R8_WITH_RELOCATED_DEPS_JAR using normal R8_WITH_RELOCATED_DEPS_JAR to dex,
@@ -66,67 +121,80 @@
// We use extra VM parameters for memory. The command parameters should look like:
// -Xmx512m com...R8 --release --min-api 1 --output path/to/folder --lib rt.jar
// --pg-conf R8KeepRules r8.jar
- // The 512m memory is required to make it work on ART since default is lower.
+ // The 512m memory is required to run on ART but any higher and the runtime will fail too.
- File ouputFolder = temp.newFolder("output");
+ Path outputFolder = temp.newFolder("output").toPath();
+ Path outputThroughCf = outputFolder.resolve("outThroughCf.zip");
- // Compile R8 to dex on the JVM.
- Path outputThroughCf = ouputFolder.toPath().resolve("outThroughCf.zip").toAbsolutePath();
- if (testExternal) {
+ // First run compiles with R8 in process and thus with assertions.
+ {
+ long start = System.nanoTime();
+ // Manually construct the R8 command as the test builder will change defaults compared
+ // to the CLI invocation (eg, compressed and pg-map output).
+ Builder builder = R8Command.builder().setOutput(outputThroughCf, OutputMode.DexIndexed);
+ getSharedBuilder().accept(builder);
+ R8.run(builder.build());
+ printTime("R8/JVM in-process", start);
+ }
+
+ // Second run compiles with R8 externally checking it to be equal with the first compilation.
+ // If this fails, likely due to non-determinism, then that is much faster than waiting for the
+ // Art run compilation to finish if the same error can be found directly.
+ {
+ long start = System.nanoTime();
+ Path outputThroughCfExternal = outputFolder.resolve("outThroughCf_external.zip");
+ Path r8jar = ToolHelper.R8_WITH_RELOCATED_DEPS_JAR;
ProcessResult javaProcessResult =
ToolHelper.runJava(
TestRuntime.getCheckedInJdk9(),
- Collections.singletonList(ToolHelper.R8_WITH_RELOCATED_DEPS_JAR),
- "-Xmx512m",
- R8.class.getTypeName(),
- "--release",
- "--min-api",
- Integer.toString(parameters.getApiLevel().getLevel()),
- "--output",
- outputThroughCf.toString(),
- "--lib",
- ToolHelper.JAVA_8_RUNTIME,
- "--pg-conf",
- R8_KEEP,
- ToolHelper.R8_WITH_RELOCATED_DEPS_JAR.toAbsolutePath().toString());
+ Collections.singletonList(r8jar),
+ ImmutableList.builder()
+ .add("-Xmx1g")
+ .add(R8.class.getTypeName())
+ .add("--output")
+ .add(commandLinePathFor(outputThroughCfExternal))
+ .addAll(getSharedArguments())
+ .build()
+ .toArray(new String[0]));
+ printTime("R8/JVM external", start);
assertEquals(javaProcessResult.toString(), 0, javaProcessResult.exitCode);
- } else {
- testForR8(parameters.getBackend())
- .addProgramFiles(ToolHelper.R8_WITH_RELOCATED_DEPS_JAR)
- .addLibraryFiles(ToolHelper.getJava8RuntimeJar())
- .addKeepRuleFiles(Paths.get(R8_KEEP))
- .setMinApi(parameters.getApiLevel())
- .compile()
- .writeToZip(outputThroughCf);
+ assertTrue(
+ "The output of R8/JVM in-process and R8/JVM external differ."
+ + " Make sure you have an up-to-date compilation of "
+ + r8jar
+ + ". If not, that could very likely cause the in-process run (eg, via intellij) to"
+ + " differ from the external run which uses "
+ + r8jar
+ + ". If up-to-date, the likely cause of this error is that R8 is non-deterministic.",
+ BootstrapCurrentEqualityTest.filesAreEqual(outputThroughCf, outputThroughCfExternal));
}
- // Compile R8 to Dex on Dex, using the previous dex artifact.
+ // Finally compile R8 on the ART runtime using the already compiled DEX version of R8.
// We need the extra parameter --64 to use 64 bits frameworks.
- Path ouputThroughDex = ouputFolder.toPath().resolve("outThroughDex.zip").toAbsolutePath();
- ProcessResult artProcessResult =
- ToolHelper.runArtRaw(
- Collections.singletonList(outputThroughCf.toAbsolutePath().toString()),
- R8.class.getTypeName(),
- (ToolHelper.ArtCommandBuilder builder) ->
- builder.appendArtOption("--64").appendArtOption("-Xmx512m"),
- parameters.getRuntime().asDex().getVm(),
- true,
- "--release",
- "--min-api",
- Integer.toString(parameters.getApiLevel().getLevel()),
- "--output",
- ouputThroughDex.toString(),
- "--lib",
- commandLinePathFor(ToolHelper.JAVA_8_RUNTIME),
- "--pg-conf",
- commandLinePathFor(R8_KEEP),
- ToolHelper.R8_WITH_RELOCATED_DEPS_JAR.toAbsolutePath().toString());
- if (artProcessResult.exitCode != 0) {
- System.out.println(artProcessResult);
+ {
+ long start = System.nanoTime();
+ Path outputThroughDex = outputFolder.resolve("outThroughDex.zip");
+ ProcessResult artProcessResult =
+ ToolHelper.runArtRaw(
+ Collections.singletonList(commandLinePathFor(outputThroughCf)),
+ R8.class.getTypeName(),
+ builder -> builder.appendArtOption("--64").appendArtOption("-Xmx512m"),
+ parameters.getRuntime().asDex().getVm(),
+ true,
+ ImmutableList.builder()
+ .add("--output")
+ .add(commandLinePathFor(outputThroughDex))
+ .addAll(getSharedArguments())
+ .build()
+ .toArray(new String[0]));
+ printTime("R8/ART", start);
+ if (artProcessResult.exitCode != 0) {
+ System.out.println(artProcessResult);
+ }
+ assertEquals(0, artProcessResult.exitCode);
+ assertTrue(
+ "The output of R8/JVM in-process and R8/ART external differ.",
+ BootstrapCurrentEqualityTest.filesAreEqual(outputThroughCf, outputThroughDex));
}
- assertEquals(0, artProcessResult.exitCode);
-
- // Ensure both generated artifacts are equal.
- assertTrue(BootstrapCurrentEqualityTest.filesAreEqual(outputThroughCf, ouputThroughDex));
}
}
diff --git a/src/test/java/com/android/tools/r8/desugar/enclosingmethod/EnclosingMethodRewriteTest.java b/src/test/java/com/android/tools/r8/desugar/enclosingmethod/EnclosingMethodRewriteTest.java
index 897f4a7..3e60058 100644
--- a/src/test/java/com/android/tools/r8/desugar/enclosingmethod/EnclosingMethodRewriteTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/enclosingmethod/EnclosingMethodRewriteTest.java
@@ -3,20 +3,26 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.desugar.enclosingmethod;
-import static org.hamcrest.CoreMatchers.containsString;
+import static com.android.tools.r8.ir.desugar.InterfaceMethodRewriter.COMPANION_CLASS_NAME_SUFFIX;
+import static com.android.tools.r8.ir.desugar.InterfaceMethodRewriter.DEFAULT_METHOD_PREFIX;
+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.ClassFileConsumer.ArchiveConsumer;
-import com.android.tools.r8.CompilationFailedException;
import com.android.tools.r8.R8FullTestBuilder;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
-import com.android.tools.r8.ToolHelper.DexVm;
+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.ClassSubject;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import com.android.tools.r8.utils.codeinspector.MethodSubject;
+import com.google.common.collect.ImmutableList;
import java.nio.file.Path;
import java.util.Collection;
+import java.util.List;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
@@ -52,11 +58,6 @@
@RunWith(Parameterized.class)
public class EnclosingMethodRewriteTest extends TestBase {
private static final Class<?> MAIN = TestClass.class;
- private static final String JAVA_OUTPUT = StringUtils.lines(
- "interface " + A.class.getName(),
- "public default int " + A.class.getName() + ".def()",
- "42"
- );
private final TestParameters parameters;
private final boolean enableMinification;
@@ -64,8 +65,7 @@
@Parameterized.Parameters(name = "{0} minification: {1}")
public static Collection<Object[]> data() {
return buildParameters(
- getTestParameters().withAllRuntimes().withAllApiLevels().build(),
- BooleanUtils.values());
+ getTestParameters().withAllRuntimesAndApiLevels().build(), BooleanUtils.values());
}
public EnclosingMethodRewriteTest(TestParameters parameters, boolean enableMinification) {
@@ -79,7 +79,7 @@
testForJvm()
.addTestClasspath()
.run(parameters.getRuntime(), MAIN)
- .assertSuccessWithOutput(JAVA_OUTPUT);
+ .assertSuccessWithOutputLines(getExpectedOutput());
}
@Test
@@ -88,7 +88,8 @@
Path desugared = temp.newFile("desugared.jar").toPath().toAbsolutePath();
R8FullTestBuilder builder =
testForR8(parameters.getBackend())
- .addProgramClasses(A.class, C.class, MAIN)
+ .addProgramClassesAndInnerClasses(A.class)
+ .addProgramClasses(C.class, MAIN)
.addKeepMainRule(MAIN)
.addKeepRules("-keepattributes InnerClasses,EnclosingMethod")
.setProgramConsumer(new ArchiveConsumer(desugared))
@@ -99,41 +100,100 @@
builder.addKeepAllClassesRule();
}
builder.compile().assertNoMessages();
- try {
- testForProguard()
- .addProgramFiles(desugared)
- .addKeepMainRule(MAIN)
- .addKeepRules("-keepattributes InnerClasses,EnclosingMethod")
- .run(parameters.getRuntime(), MAIN)
- .assertSuccessWithOutput(JAVA_OUTPUT);
- } catch (CompilationFailedException e) {
- // TODO(b/70293332)
- assertThat(e.getMessage(), containsString("unresolved references"));
- assertThat(e.getMessage(), containsString(A.class.getName() + "$1"));
- }
+ testForProguard()
+ .addProgramFiles(desugared)
+ .addKeepMainRule(MAIN)
+ .addKeepRules("-keepattributes InnerClasses,EnclosingMethod")
+ .run(parameters.getRuntime(), MAIN)
+ .assertSuccess();
}
@Test
public void testR8() throws Exception {
R8FullTestBuilder builder =
testForR8(parameters.getBackend())
- .addProgramClasses(A.class, C.class, MAIN)
+ .addProgramClassesAndInnerClasses(A.class)
+ .addProgramClasses(C.class, MAIN)
.addKeepMainRule(MAIN)
.addKeepRules("-keepattributes InnerClasses,EnclosingMethod")
.setMinApi(parameters.getApiLevel());
if (enableMinification) {
builder.addKeepAllClassesRuleWithAllowObfuscation();
} else {
- builder.addKeepAllClassesRule();
- }
- String errorType = "ClassNotFoundException";
- if (parameters.isDexRuntime()
- && parameters.getRuntime().asDex().getVm().isOlderThanOrEqual(DexVm.ART_4_4_4_HOST)) {
- errorType = "NoClassDefFoundError";
+ builder.noTreeShaking().noMinification();
}
builder
+ .compile()
+ .inspect(
+ inspect -> {
+ ClassSubject cImplSubject = inspect.clazz(A.class.getTypeName() + "$1");
+ assertThat(cImplSubject, isPresent());
+
+ ClassSubject enclosingClassSubject =
+ inspect.clazz(
+ parameters.canUseDefaultAndStaticInterfaceMethods()
+ ? A.class.getTypeName()
+ : A.class.getTypeName() + COMPANION_CLASS_NAME_SUFFIX);
+ assertThat(enclosingClassSubject, isPresent());
+ assertEquals(
+ enclosingClassSubject.getDexProgramClass().getType(),
+ cImplSubject
+ .getDexProgramClass()
+ .getEnclosingMethodAttribute()
+ .getEnclosingMethod()
+ .getHolderType());
+ })
.run(parameters.getRuntime(), MAIN)
- // TODO(b/70293332)
- .assertFailureWithErrorThatMatches(containsString(errorType));
+ .apply(
+ result -> result.assertSuccessWithOutputLines(getExpectedOutput(result.inspector())));
+ }
+
+ private List<String> getExpectedOutput() {
+ return ImmutableList.of(
+ "interface " + A.class.getTypeName(),
+ "public default int " + A.class.getTypeName() + ".def()",
+ "42");
+ }
+
+ private List<String> getExpectedOutput(CodeInspector inspector) {
+ ClassSubject aClassSubject = inspector.clazz(A.class);
+ assertThat(aClassSubject, isPresent());
+
+ MethodSubject defMethodSubject = aClassSubject.uniqueMethodWithName("def");
+ assertThat(defMethodSubject, isPresent());
+
+ if (parameters.canUseDefaultAndStaticInterfaceMethods()) {
+ String modifiers =
+ parameters.isCfRuntime()
+ || parameters.getDexRuntimeVersion().isNewerThanOrEqual(Version.V8_1_0)
+ ? "public default"
+ : "public";
+ return ImmutableList.of(
+ "interface " + aClassSubject.getFinalName(),
+ modifiers
+ + " int "
+ + aClassSubject.getFinalName()
+ + "."
+ + defMethodSubject.getFinalName()
+ + "()",
+ "42");
+ }
+
+ ClassSubject aCompanionClassSubject =
+ inspector.clazz(A.class.getTypeName() + COMPANION_CLASS_NAME_SUFFIX);
+ assertThat(aCompanionClassSubject, isPresent());
+
+ String methodNamePrefix = enableMinification ? "" : DEFAULT_METHOD_PREFIX;
+ return ImmutableList.of(
+ "class " + aCompanionClassSubject.getFinalName(),
+ "public static int "
+ + aCompanionClassSubject.getFinalName()
+ + "."
+ + methodNamePrefix
+ + defMethodSubject.getFinalName()
+ + "("
+ + aClassSubject.getFinalName()
+ + ")",
+ "42");
}
}
diff --git a/src/test/java/com/android/tools/r8/desugar/lambdas/DeduplicateLambdasWithDefaultMethodsTest.java b/src/test/java/com/android/tools/r8/desugar/lambdas/DeduplicateLambdasWithDefaultMethodsTest.java
new file mode 100644
index 0000000..b252cf4
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/desugar/lambdas/DeduplicateLambdasWithDefaultMethodsTest.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.desugar.lambdas;
+
+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.android.tools.r8.references.Reference;
+import com.android.tools.r8.synthesis.SyntheticItemsTestUtils;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.codeinspector.FoundClassSubject;
+import com.google.common.collect.ImmutableSet;
+import java.util.stream.Collectors;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class DeduplicateLambdasWithDefaultMethodsTest extends TestBase {
+
+ @Parameterized.Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withNoneRuntime().build();
+ }
+
+ public DeduplicateLambdasWithDefaultMethodsTest(TestParameters parameters) {
+ parameters.assertNoneRuntime();
+ }
+
+ @Test
+ public void test() throws Exception {
+ assertEquals(
+ ImmutableSet.of(
+ Reference.classFromClass(I.class),
+ Reference.classFromClass(TestClass.class),
+ SyntheticItemsTestUtils.syntheticCompanionClass(I.class),
+ SyntheticItemsTestUtils.syntheticLambdaClass(TestClass.class, 0)),
+ testForD8(Backend.CF)
+ .addInnerClasses(getClass())
+ .setIntermediate(true)
+ .setMinApi(AndroidApiLevel.B)
+ .compile()
+ .inspector()
+ .allClasses()
+ .stream()
+ .map(FoundClassSubject::getFinalReference)
+ .collect(Collectors.toSet()));
+ }
+
+ interface I {
+ void foo();
+
+ // Lots of methods which may cause the ordering of methods on a class to change between builds.
+ default void a() {
+ System.out.print("a");
+ }
+
+ default void b() {
+ System.out.print("b");
+ }
+
+ default void c() {
+ System.out.print("c");
+ }
+
+ default void x() {
+ System.out.print("x");
+ }
+
+ default void y() {
+ System.out.print("y");
+ }
+
+ default void z() {
+ System.out.print("z");
+ }
+ }
+
+ public static class TestClass {
+ private static void foo() {
+ System.out.println("foo");
+ }
+
+ private static void pI(I i) {
+ i.a();
+ i.b();
+ i.c();
+ i.x();
+ i.y();
+ i.z();
+ i.foo();
+ }
+
+ public static void main(String[] args) {
+ // Duplication of the same lambda, each of which should become a shared instance.
+ pI(TestClass::foo);
+ pI(TestClass::foo);
+ pI(TestClass::foo);
+ pI(TestClass::foo);
+ pI(TestClass::foo);
+ pI(TestClass::foo);
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/desugar/lambdas/LambdaEqualityTest.java b/src/test/java/com/android/tools/r8/desugar/lambdas/LambdaEqualityTest.java
new file mode 100644
index 0000000..90c8009
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/desugar/lambdas/LambdaEqualityTest.java
@@ -0,0 +1,140 @@
+// 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.lambdas;
+
+import static org.junit.Assume.assumeTrue;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.references.Reference;
+import com.android.tools.r8.utils.StringUtils;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+/**
+ * These tests document the behavior of lambdas w.r.t identity and equality.
+ *
+ * <p>The D8 and R8 compilers take the stance that a program should not rely on either identity or
+ * equality of any lambda metafactory allocated lambda. Thus the status of these tests differ
+ * between JVM, D8/CF, D8/DEX and R8 runs as the compilers may or may not share classes and
+ * allocations as seen fit.
+ */
+@RunWith(Parameterized.class)
+public class LambdaEqualityTest extends TestBase {
+
+ static final String EXPECTED_JAVAC =
+ StringUtils.lines(
+ "Same method refs",
+ "true",
+ "true",
+ "true",
+ "Different method refs",
+ "false",
+ "false",
+ "false",
+ "Empty lambda",
+ "false",
+ "false",
+ "false");
+
+ static final String EXPECTED_D8 =
+ StringUtils.lines(
+ "Same method refs",
+ "true",
+ "true",
+ "true",
+ "Different method refs",
+ "true", // D8 will share the class for the method references.
+ "false",
+ "false",
+ "Empty lambda",
+ "false",
+ "false",
+ "false");
+
+ static final String EXPECTED_R8 =
+ StringUtils.lines(
+ "Same method refs",
+ "true",
+ "true",
+ "true",
+ "Different method refs",
+ "true", // R8 will share the class for the method references.
+ "false",
+ "false",
+ "Empty lambda",
+ "true", // R8 will eliminate the call to the impl method thus making lambdas equal.
+ "true",
+ "true");
+
+ private final TestParameters parameters;
+
+ @Parameterized.Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimes().withAllApiLevelsAlsoForCf().build();
+ }
+
+ public LambdaEqualityTest(TestParameters parameters) {
+ this.parameters = parameters;
+ }
+
+ @Test
+ public void testJvm() throws Exception {
+ assumeTrue(parameters.isCfRuntime());
+ testForRuntime(parameters)
+ .addInnerClasses(LambdaEqualityTest.class)
+ .run(parameters.getRuntime(), TestClass.class)
+ .assertSuccessWithOutput(EXPECTED_JAVAC);
+ }
+
+ @Test
+ public void testD8() throws Exception {
+ testForD8(parameters.getBackend())
+ .addInnerClasses(LambdaEqualityTest.class)
+ .setMinApi(parameters.getApiLevel())
+ .run(parameters.getRuntime(), TestClass.class)
+ .assertSuccessWithOutput(EXPECTED_D8);
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ testForR8(parameters.getBackend())
+ .addInnerClasses(LambdaEqualityTest.class)
+ .setMinApi(parameters.getApiLevel())
+ .addKeepMainRule(TestClass.class)
+ .addKeepMethodRules(
+ Reference.methodFromMethod(
+ TestClass.class.getDeclaredMethod(
+ "compare", String.class, MyInterface.class, MyInterface.class)))
+ .run(parameters.getRuntime(), TestClass.class)
+ // The use of invoke dynamics prohibits the optimization and sharing of lambdas in R8.
+ .assertSuccessWithOutput(parameters.isCfRuntime() ? EXPECTED_JAVAC : EXPECTED_R8);
+ }
+
+ interface MyInterface {
+ void foo();
+ }
+
+ static class TestClass {
+
+ public static void compare(String msg, MyInterface i1, MyInterface i2) {
+ System.out.println(msg);
+ System.out.println(i1.getClass() == i2.getClass());
+ System.out.println(i1 == i2);
+ System.out.println(i1.equals(i2));
+ }
+
+ public static void main(String[] args) {
+ MyInterface println = System.out::println;
+ // These lambdas are physically the same and should remain so in all cases.
+ compare("Same method refs", println, println);
+ // These lambdas can be shared as they reference the same actual function.
+ compare("Different method refs", println, System.out::println);
+ // These lambdas cannot be shared (by D8) as javac will generate a lambda$main$X for each.
+ compare("Empty lambda", () -> {}, () -> {});
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/desugar/lambdas/LambdaNamingConflictTest.java b/src/test/java/com/android/tools/r8/desugar/lambdas/LambdaNamingConflictTest.java
new file mode 100644
index 0000000..1a6e364
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/desugar/lambdas/LambdaNamingConflictTest.java
@@ -0,0 +1,113 @@
+// 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.lambdas;
+
+import static org.junit.Assume.assumeTrue;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.references.ClassReference;
+import com.android.tools.r8.synthesis.SyntheticItemsTestUtils;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.StringUtils;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class LambdaNamingConflictTest extends TestBase {
+
+ static final String EXPECTED = StringUtils.lines("boo!");
+
+ @Parameterized.Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters()
+ .withAllRuntimes()
+ .withApiLevel(AndroidApiLevel.B)
+ .enableApiLevelsForCf()
+ .build();
+ }
+
+ // The expected synthetic name is the context of the lambda, TestClass, and the first id.
+ private static final ClassReference CONFLICTING_NAME =
+ SyntheticItemsTestUtils.syntheticLambdaClass(TestClass.class, 0);
+
+ private final TestParameters parameters;
+
+ public LambdaNamingConflictTest(TestParameters parameters) {
+ this.parameters = parameters;
+ }
+
+ @Test
+ public void testJvm() throws Exception {
+ assumeTrue(parameters.isCfRuntime());
+ testForJvm()
+ .addProgramClasses(I.class)
+ .addProgramClassFileData(getConflictingNameClass())
+ .addProgramClassFileData(getTransformedMainClass())
+ .run(parameters.getRuntime(), TestClass.class)
+ .assertSuccessWithOutput(EXPECTED);
+ }
+
+ @Test
+ public void testD8() throws Exception {
+ testForD8(parameters.getBackend())
+ .addProgramClasses(I.class)
+ .addProgramClassFileData(getConflictingNameClass())
+ .addProgramClassFileData(getTransformedMainClass())
+ .setMinApi(parameters.getApiLevel())
+ .addOptionsModification(o -> o.testing.allowConflictingSyntheticTypes = true)
+ .run(parameters.getRuntime(), TestClass.class)
+ .assertSuccessWithOutput(EXPECTED);
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ testForR8(parameters.getBackend())
+ .addProgramClasses(I.class)
+ .addProgramClassFileData(getConflictingNameClass())
+ .addProgramClassFileData(getTransformedMainClass())
+ .setMinApi(parameters.getApiLevel())
+ .addOptionsModification(o -> o.testing.allowConflictingSyntheticTypes = true)
+ .addKeepMainRule(TestClass.class)
+ // Ensure that R8 cannot remove or rename the conflicting name.
+ .addKeepClassAndMembersRules(CONFLICTING_NAME.getTypeName())
+ .run(parameters.getRuntime(), TestClass.class)
+ .assertSuccessWithOutput(EXPECTED);
+ }
+
+ private byte[] getTransformedMainClass() throws Exception {
+ return transformer(TestClass.class)
+ .transformMethodInsnInMethod(
+ "main",
+ (opcode, owner, name, descriptor, isInterface, visitor) ->
+ visitor.visitMethodInsn(
+ opcode, CONFLICTING_NAME.getBinaryName(), name, descriptor, isInterface))
+ .transform();
+ }
+
+ private byte[] getConflictingNameClass() throws Exception {
+ return transformer(WillBeConflictingName.class)
+ .setClassDescriptor(CONFLICTING_NAME.getDescriptor())
+ .transform();
+ }
+
+ interface I {
+ void bar();
+ }
+
+ static class WillBeConflictingName {
+ public static void foo(I i) {
+ i.bar();
+ }
+ }
+
+ static class TestClass {
+
+ public static void main(String[] args) {
+ WillBeConflictingName.foo(() -> System.out.println("boo!"));
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/desugar/lambdas/LambdaStaticInstanceFieldDuplicationTest.java b/src/test/java/com/android/tools/r8/desugar/lambdas/LambdaStaticInstanceFieldDuplicationTest.java
new file mode 100644
index 0000000..ac5b076
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/desugar/lambdas/LambdaStaticInstanceFieldDuplicationTest.java
@@ -0,0 +1,281 @@
+// 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.lambdas;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assume.assumeTrue;
+
+import com.android.tools.r8.OutputMode;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.references.MethodReference;
+import com.android.tools.r8.synthesis.SyntheticItemsTestUtils;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.StringUtils;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import com.android.tools.r8.utils.codeinspector.InstructionSubject;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Sets;
+import com.google.common.collect.Sets.SetView;
+import java.nio.file.Path;
+import java.util.HashSet;
+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;
+
+@RunWith(Parameterized.class)
+public class LambdaStaticInstanceFieldDuplicationTest extends TestBase {
+
+ static final String EXPECTED = StringUtils.lines("User1.1", "User1.2", "User2");
+
+ static final List<Class<?>> CLASSES =
+ ImmutableList.of(TestClass.class, MyConsumer.class, Accept.class, User1.class, User2.class);
+
+ static final List<String> CLASS_TYPE_NAMES =
+ CLASSES.stream().map(Class::getTypeName).collect(Collectors.toList());
+
+ private final TestParameters parameters;
+
+ @Parameterized.Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters()
+ .withAllRuntimes()
+ .withApiLevel(AndroidApiLevel.J)
+ .enableApiLevelsForCf()
+ .build();
+ }
+
+ public LambdaStaticInstanceFieldDuplicationTest(TestParameters parameters) {
+ this.parameters = parameters;
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ // R8 does not support desugaring with class file output so this test is only valid for DEX.
+ assumeTrue(parameters.isDexRuntime());
+ runR8(false);
+ runR8(true);
+ }
+
+ private void runR8(boolean minify) throws Exception {
+ testForR8(parameters.getBackend())
+ .addProgramClasses(CLASSES)
+ .addKeepMainRule(TestClass.class)
+ // Prevent R8 from eliminating the lambdas by keeping the application of them.
+ .addKeepClassAndMembersRules(Accept.class)
+ .setMinApi(parameters.getApiLevel())
+ .minification(minify)
+ .run(parameters.getRuntime(), TestClass.class)
+ .assertSuccessWithOutput(EXPECTED)
+ .inspect(this::checkNoOriginalsAndNoInternalSynthetics);
+ }
+
+ @Test
+ public void testD8() throws Exception {
+ testForD8(parameters.getBackend())
+ .addProgramClasses(CLASSES)
+ .setMinApi(parameters.getApiLevel())
+ .run(parameters.getRuntime(), TestClass.class)
+ .assertSuccessWithOutput(EXPECTED)
+ .inspect(this::checkNoOriginalsAndNoInternalSynthetics)
+ .inspect(this::checkExpectedSynthetics);
+ ;
+ }
+
+ @Test
+ public void testD8Merging() throws Exception {
+ assumeTrue(
+ "b/147485959: Merging does not happen for CF due to lack of synthetic annotations",
+ parameters.isDexRuntime());
+ boolean intermediate = true;
+ runD8Merging(intermediate);
+ }
+
+ @Test
+ public void testD8MergingNonIntermediate() throws Exception {
+ boolean intermediate = false;
+ runD8Merging(intermediate);
+ }
+
+ private void runD8Merging(boolean intermediate) throws Exception {
+ // Compile part 1 of the input (maybe intermediate)
+ Path out1 =
+ testForD8(parameters.getBackend())
+ .addProgramClasses(User1.class)
+ .addClasspathClasses(CLASSES)
+ .setMinApi(parameters.getApiLevel())
+ .setIntermediate(intermediate)
+ .compile()
+ .writeToZip();
+
+ // Compile part 2 of the input (maybe intermediate)
+ Path out2 =
+ testForD8(parameters.getBackend())
+ .addProgramClasses(User2.class)
+ .addClasspathClasses(CLASSES)
+ .setMinApi(parameters.getApiLevel())
+ .setIntermediate(intermediate)
+ .compile()
+ .writeToZip();
+
+ SetView<MethodReference> syntheticsInParts =
+ Sets.union(
+ getSyntheticMethods(new CodeInspector(out1)),
+ getSyntheticMethods(new CodeInspector(out2)));
+
+ // Merge parts as an intermediate artifact.
+ // This will not merge synthetics regardless of the setting of intermediate.
+ Path out3 = temp.newFolder().toPath().resolve("out3.zip");
+ testForD8(parameters.getBackend())
+ .addProgramClasses(TestClass.class, MyConsumer.class, Accept.class)
+ .addProgramFiles(out1, out2)
+ .setMinApi(parameters.getApiLevel())
+ .setIntermediate(true)
+ .compile()
+ .writeToZip(out3)
+ .run(parameters.getRuntime(), TestClass.class)
+ .assertSuccessWithOutput(EXPECTED)
+ .inspect(this::checkNoOriginalsAndNoInternalSynthetics)
+ .inspect(inspector -> assertEquals(syntheticsInParts, getSyntheticMethods(inspector)));
+
+ // Finally do a non-intermediate merge.
+ testForD8(parameters.getBackend())
+ .addProgramFiles(out3)
+ .setMinApi(parameters.getApiLevel())
+ .run(parameters.getRuntime(), TestClass.class)
+ .assertSuccessWithOutput(EXPECTED)
+ .inspect(this::checkNoOriginalsAndNoInternalSynthetics)
+ .inspect(
+ inspector -> {
+ if (intermediate) {
+ // If all previous builds where intermediate then synthetics are merged.
+ checkExpectedSynthetics(inspector);
+ } else {
+ // Otherwise merging non-intermediate artifacts, synthetics will not be identified.
+ // Check that they are exactly as in the part inputs.
+ assertEquals(syntheticsInParts, getSyntheticMethods(inspector));
+ }
+ });
+ }
+
+ @Test
+ public void testD8FilePerClassFile() throws Exception {
+ assumeTrue(parameters.isDexRuntime());
+ runD8FilePerMode(OutputMode.DexFilePerClassFile);
+ }
+
+ @Test
+ public void testD8FilePerClass() throws Exception {
+ assumeTrue(parameters.isDexRuntime());
+ runD8FilePerMode(OutputMode.DexFilePerClass);
+ }
+
+ public void runD8FilePerMode(OutputMode outputMode) throws Exception {
+ Path perClassOutput =
+ testForD8(parameters.getBackend())
+ .setOutputMode(outputMode)
+ .addProgramClasses(CLASSES)
+ .setMinApi(parameters.getApiLevel())
+ .compile()
+ .writeToZip();
+ testForD8()
+ .addProgramFiles(perClassOutput)
+ .setMinApi(parameters.getApiLevel())
+ .run(parameters.getRuntime(), TestClass.class)
+ .assertSuccessWithOutput(EXPECTED)
+ .inspect(this::checkNoOriginalsAndNoInternalSynthetics)
+ .inspect(this::checkExpectedSynthetics);
+ }
+
+ private void checkNoOriginalsAndNoInternalSynthetics(CodeInspector inspector) {
+ inspector.forAllClasses(
+ clazz -> {
+ assertFalse(SyntheticItemsTestUtils.isInternalLambda(clazz.getFinalReference()));
+ clazz.forAllMethods(
+ method ->
+ assertTrue(
+ "Unexpected invoke dynamic:\n" + method.getMethod().codeToString(),
+ method.isAbstract()
+ || method
+ .streamInstructions()
+ .noneMatch(InstructionSubject::isInvokeDynamic)));
+ });
+ }
+
+ private Set<MethodReference> getSyntheticMethods(CodeInspector inspector) {
+ Set<MethodReference> methods = new HashSet<>();
+ inspector.allClasses().stream()
+ .filter(c -> !CLASS_TYPE_NAMES.contains(c.getFinalName()))
+ .forEach(
+ c ->
+ c.allMethods(m -> !m.isInstanceInitializer() && !m.isClassInitializer())
+ .forEach(m -> methods.add(m.asMethodReference())));
+ return methods;
+ }
+
+ private void checkExpectedSynthetics(CodeInspector inspector) throws Exception {
+ // Hardcoded set of expected synthetics in a "final" build. This set could change if the
+ // compiler makes any changes to the naming, sorting or grouping of synthetics. It is hard-coded
+ // here to check that the compiler generates this deterministically for any single run or merge
+ // of intermediates.
+ Set<MethodReference> expectedSynthetics =
+ ImmutableSet.of(
+ // User1 has two lambdas.
+ SyntheticItemsTestUtils.syntheticLambdaMethod(
+ User1.class, 0, MyConsumer.class.getMethod("accept", Object.class)),
+ SyntheticItemsTestUtils.syntheticLambdaMethod(
+ User1.class, 1, MyConsumer.class.getMethod("accept", Object.class)),
+ // User2 has one lambda.
+ SyntheticItemsTestUtils.syntheticLambdaMethod(
+ User2.class, 0, MyConsumer.class.getMethod("accept", Object.class)));
+ assertEquals(expectedSynthetics, getSyntheticMethods(inspector));
+ }
+
+ interface MyConsumer {
+ void accept(Object o);
+ }
+
+ static class Accept {
+ public static void accept(Object o, MyConsumer consumer) {
+ consumer.accept(o);
+ }
+ }
+
+ static class User1 {
+
+ private static void testSystemPrintln1() {
+ // The lambda will reference a lambda$x method on User1 which is created by javac for each
+ // lambda on the class. Thus there can be no sharing unless R8 inlines the lambda method into
+ // the desugared lambda classes.
+ Accept.accept("1.1", o -> System.out.println("User" + o));
+ }
+
+ private static void testSystemPrintln2() {
+ Accept.accept("1.2", o -> System.out.println("User" + o));
+ }
+ }
+
+ static class User2 {
+
+ private static void testSystemPrintln() {
+ Accept.accept("2", o -> System.out.println("User" + o));
+ }
+ }
+
+ static class TestClass {
+
+ public static void main(String[] args) {
+ User1.testSystemPrintln1();
+ User1.testSystemPrintln2();
+ User2.testSystemPrintln();
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/desugar/lambdas/LambdaToSysOutPrintlnDuplicationTest.java b/src/test/java/com/android/tools/r8/desugar/lambdas/LambdaToSysOutPrintlnDuplicationTest.java
new file mode 100644
index 0000000..5b73125
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/desugar/lambdas/LambdaToSysOutPrintlnDuplicationTest.java
@@ -0,0 +1,265 @@
+// 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.lambdas;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assume.assumeTrue;
+
+import com.android.tools.r8.OutputMode;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.references.MethodReference;
+import com.android.tools.r8.synthesis.SyntheticItemsTestUtils;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.StringUtils;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import com.android.tools.r8.utils.codeinspector.InstructionSubject;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Sets;
+import com.google.common.collect.Sets.SetView;
+import java.nio.file.Path;
+import java.util.HashSet;
+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;
+
+@RunWith(Parameterized.class)
+public class LambdaToSysOutPrintlnDuplicationTest extends TestBase {
+
+ static final String EXPECTED = StringUtils.lines("User1", "User2");
+
+ static final List<Class<?>> CLASSES =
+ ImmutableList.of(TestClass.class, MyConsumer.class, Accept.class, User1.class, User2.class);
+
+ static final List<String> CLASS_TYPE_NAMES =
+ CLASSES.stream().map(Class::getTypeName).collect(Collectors.toList());
+
+ private final TestParameters parameters;
+
+ @Parameterized.Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters()
+ .withAllRuntimes()
+ .withApiLevel(AndroidApiLevel.J)
+ .enableApiLevelsForCf()
+ .build();
+ }
+
+ public LambdaToSysOutPrintlnDuplicationTest(TestParameters parameters) {
+ this.parameters = parameters;
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ // R8 does not support desugaring with class file output so this test is only valid for DEX.
+ assumeTrue(parameters.isDexRuntime());
+ runR8(false);
+ runR8(true);
+ }
+
+ private void runR8(boolean minify) throws Exception {
+ testForR8(parameters.getBackend())
+ .addProgramClasses(CLASSES)
+ .addKeepMainRule(TestClass.class)
+ .setMinApi(parameters.getApiLevel())
+ .minification(minify)
+ .run(parameters.getRuntime(), TestClass.class)
+ .assertSuccessWithOutput(EXPECTED)
+ .inspect(this::checkNoOriginalsAndNoInternalSynthetics);
+ }
+
+ @Test
+ public void testD8() throws Exception {
+ testForD8(parameters.getBackend())
+ .addProgramClasses(CLASSES)
+ .setMinApi(parameters.getApiLevel())
+ .run(parameters.getRuntime(), TestClass.class)
+ .assertSuccessWithOutput(EXPECTED)
+ .inspect(this::checkNoOriginalsAndNoInternalSynthetics)
+ .inspect(this::checkExpectedSynthetics);
+ ;
+ }
+
+ @Test
+ public void testD8Merging() throws Exception {
+ assumeTrue(
+ "b/147485959: Merging does not happen for CF due to lack of synthetic annotations",
+ parameters.isDexRuntime());
+ boolean intermediate = true;
+ runD8Merging(intermediate);
+ }
+
+ @Test
+ public void testD8MergingNonIntermediate() throws Exception {
+ boolean intermediate = false;
+ runD8Merging(intermediate);
+ }
+
+ private void runD8Merging(boolean intermediate) throws Exception {
+ // Compile part 1 of the input (maybe intermediate)
+ Path out1 =
+ testForD8(parameters.getBackend())
+ .addProgramClasses(User1.class)
+ .addClasspathClasses(CLASSES)
+ .setMinApi(parameters.getApiLevel())
+ .setIntermediate(intermediate)
+ .compile()
+ .writeToZip();
+
+ // Compile part 2 of the input (maybe intermediate)
+ Path out2 =
+ testForD8(parameters.getBackend())
+ .addProgramClasses(User2.class)
+ .addClasspathClasses(CLASSES)
+ .setMinApi(parameters.getApiLevel())
+ .setIntermediate(intermediate)
+ .compile()
+ .writeToZip();
+
+ SetView<MethodReference> syntheticsInParts =
+ Sets.union(
+ getSyntheticMethods(new CodeInspector(out1)),
+ getSyntheticMethods(new CodeInspector(out2)));
+
+ // Merge parts as an intermediate artifact.
+ // This will not merge synthetics regardless of the setting of intermediate.
+ Path out3 = temp.newFolder().toPath().resolve("out3.zip");
+ testForD8(parameters.getBackend())
+ .addProgramClasses(TestClass.class, MyConsumer.class, Accept.class)
+ .addProgramFiles(out1, out2)
+ .setMinApi(parameters.getApiLevel())
+ .setIntermediate(true)
+ .compile()
+ .writeToZip(out3)
+ .run(parameters.getRuntime(), TestClass.class)
+ .assertSuccessWithOutput(EXPECTED)
+ .inspect(this::checkNoOriginalsAndNoInternalSynthetics)
+ .inspect(inspector -> assertEquals(syntheticsInParts, getSyntheticMethods(inspector)));
+
+ // Finally do a non-intermediate merge.
+ testForD8(parameters.getBackend())
+ .addProgramFiles(out3)
+ .setMinApi(parameters.getApiLevel())
+ .run(parameters.getRuntime(), TestClass.class)
+ .assertSuccessWithOutput(EXPECTED)
+ .inspect(this::checkNoOriginalsAndNoInternalSynthetics)
+ .inspect(
+ inspector -> {
+ if (intermediate) {
+ // If all previous builds where intermediate then synthetics are merged.
+ checkExpectedSynthetics(inspector);
+ } else {
+ // Otherwise merging non-intermediate artifacts, synthetics will not be identified.
+ // Check that they are exactly as in the part inputs.
+ assertEquals(syntheticsInParts, getSyntheticMethods(inspector));
+ }
+ });
+ }
+
+ @Test
+ public void testD8FilePerClassFile() throws Exception {
+ assumeTrue(parameters.isDexRuntime());
+ runD8FilePerMode(OutputMode.DexFilePerClassFile);
+ }
+
+ @Test
+ public void testD8FilePerClass() throws Exception {
+ assumeTrue(parameters.isDexRuntime());
+ runD8FilePerMode(OutputMode.DexFilePerClass);
+ }
+
+ public void runD8FilePerMode(OutputMode outputMode) throws Exception {
+ Path perClassOutput =
+ testForD8(parameters.getBackend())
+ .setOutputMode(outputMode)
+ .addProgramClasses(CLASSES)
+ .setMinApi(parameters.getApiLevel())
+ .compile()
+ .writeToZip();
+ testForD8()
+ .addProgramFiles(perClassOutput)
+ .setMinApi(parameters.getApiLevel())
+ .run(parameters.getRuntime(), TestClass.class)
+ .assertSuccessWithOutput(EXPECTED)
+ .inspect(this::checkNoOriginalsAndNoInternalSynthetics)
+ .inspect(this::checkExpectedSynthetics);
+ }
+
+ private void checkNoOriginalsAndNoInternalSynthetics(CodeInspector inspector) {
+ inspector.forAllClasses(
+ clazz -> {
+ assertFalse(SyntheticItemsTestUtils.isInternalLambda(clazz.getFinalReference()));
+ clazz.forAllMethods(
+ method ->
+ assertTrue(
+ "Unexpected invoke dynamic:\n" + method.getMethod().codeToString(),
+ method.isAbstract()
+ || method
+ .streamInstructions()
+ .noneMatch(InstructionSubject::isInvokeDynamic)));
+ });
+ }
+
+ private Set<MethodReference> getSyntheticMethods(CodeInspector inspector) {
+ Set<MethodReference> methods = new HashSet<>();
+ inspector.allClasses().stream()
+ .filter(c -> !CLASS_TYPE_NAMES.contains(c.getFinalName()))
+ .forEach(
+ c ->
+ c.allMethods(m -> !m.isInstanceInitializer())
+ .forEach(m -> methods.add(m.asMethodReference())));
+ return methods;
+ }
+
+ private void checkExpectedSynthetics(CodeInspector inspector) throws Exception {
+ // Hardcoded set of expected synthetics in a "final" build. This set could change if the
+ // compiler makes any changes to the naming, sorting or grouping of synthetics. It is hard-coded
+ // here to check that the compiler generates this deterministically for any single run or merge
+ // of intermediates.
+ Set<MethodReference> expectedSynthetics =
+ ImmutableSet.of(
+ SyntheticItemsTestUtils.syntheticLambdaMethod(
+ User1.class, 0, MyConsumer.class.getMethod("accept", Object.class)));
+ assertEquals(expectedSynthetics, getSyntheticMethods(inspector));
+ }
+
+ interface MyConsumer {
+ void accept(Object o);
+ }
+
+ static class Accept {
+ public static void accept(Object o, MyConsumer consumer) {
+ consumer.accept(o);
+ }
+ }
+
+ static class User1 {
+
+ private static void testSystemPrintln() {
+ Accept.accept("User1", System.out::println);
+ }
+ }
+
+ static class User2 {
+
+ private static void testSystemPrintln() {
+ Accept.accept("User2", System.out::println);
+ }
+ }
+
+ static class TestClass {
+
+ public static void main(String[] args) {
+ User1.testSystemPrintln();
+ User2.testSystemPrintln();
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/desugar/lambdas/LegacyLambdaMergeTest.java b/src/test/java/com/android/tools/r8/desugar/lambdas/LegacyLambdaMergeTest.java
new file mode 100644
index 0000000..dd00acf
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/desugar/lambdas/LegacyLambdaMergeTest.java
@@ -0,0 +1,123 @@
+// 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.lambdas;
+
+import static org.junit.Assume.assumeTrue;
+
+import com.android.tools.r8.D8TestCompileResult;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.graph.AccessFlags;
+import com.android.tools.r8.references.ClassReference;
+import com.android.tools.r8.references.Reference;
+import com.android.tools.r8.transformers.ClassFileTransformer.MethodInsnTransform;
+import com.android.tools.r8.transformers.ClassFileTransformer.TypeInsnTransform;
+import com.android.tools.r8.utils.StringUtils;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.objectweb.asm.MethodVisitor;
+
+@RunWith(Parameterized.class)
+public class LegacyLambdaMergeTest extends TestBase {
+
+ static final String EXPECTED = StringUtils.lines("Hello, world");
+
+ private final TestParameters parameters;
+
+ @Parameterized.Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimes().withAllApiLevels().build();
+ }
+
+ public LegacyLambdaMergeTest(TestParameters parameters) {
+ this.parameters = parameters;
+ }
+
+ @Test
+ public void testReference() throws Exception {
+ assumeTrue(parameters.isCfRuntime());
+ testForJvm()
+ .addProgramClassFileData(getTransformedMain())
+ // Add the lambda twice (JVM just picks the first).
+ .addProgramClassFileData(getTransformedLambda())
+ .addProgramClassFileData(getTransformedLambda())
+ .run(parameters.getRuntime(), TestClass.class)
+ .assertSuccessWithOutput(EXPECTED);
+ }
+
+ @Test
+ public void testD8() throws Exception {
+ // Merging legacy lambdas is only valid for DEX inputs, thus also not R8 applicable.
+ assumeTrue(parameters.isDexRuntime());
+ D8TestCompileResult lambda =
+ testForD8()
+ .setMinApi(parameters.getApiLevel())
+ .addProgramClassFileData(getTransformedLambda())
+ .compile();
+ testForD8()
+ .setMinApi(parameters.getApiLevel())
+ .addProgramClassFileData(getTransformedMain())
+ // Add the lambda twice.
+ .addProgramFiles(lambda.writeToZip())
+ .addProgramFiles(lambda.writeToZip())
+ .run(parameters.getRuntime(), TestClass.class)
+ .assertSuccessWithOutput(EXPECTED);
+ }
+
+ private ClassReference LAMBDA =
+ Reference.classFromDescriptor(
+ Reference.classFromClass(WillBeLambda.class)
+ .getDescriptor()
+ .replace("WillBeLambda", "-$$Lambda$XYZ"));
+
+ private byte[] getTransformedLambda() throws Exception {
+ return transformer(WillBeLambda.class)
+ .setClassDescriptor(LAMBDA.getDescriptor())
+ .setAccessFlags(AccessFlags::setSynthetic)
+ .transform();
+ }
+
+ private byte[] getTransformedMain() throws Exception {
+ return transformer(TestClass.class)
+ .transformMethodInsnInMethod(
+ "main",
+ new MethodInsnTransform() {
+ @Override
+ public void visitMethodInsn(
+ int opcode,
+ String owner,
+ String name,
+ String descriptor,
+ boolean isInterface,
+ MethodVisitor visitor) {
+ visitor.visitMethodInsn(
+ opcode, LAMBDA.getBinaryName(), name, descriptor, isInterface);
+ }
+ })
+ .transformTypeInsnInMethod(
+ "main",
+ new TypeInsnTransform() {
+ @Override
+ public void visitTypeInsn(int opcode, String type, MethodVisitor visitor) {
+ visitor.visitTypeInsn(opcode, LAMBDA.getBinaryName());
+ }
+ })
+ .transform();
+ }
+
+ static class WillBeLambda {
+ public void foo() {
+ System.out.println("Hello, world");
+ }
+ }
+
+ static class TestClass {
+
+ public static void main(String[] args) {
+ new WillBeLambda().foo();
+ }
+ }
+}
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
new file mode 100644
index 0000000..35e31d5
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/desugar/lambdas/mergedcontext/MergedContextTest.java
@@ -0,0 +1,106 @@
+// 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.lambdas.mergedcontext;
+
+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.StringUtils;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class MergedContextTest extends TestBase {
+
+ static final String EXPECTED = StringUtils.lines("B::foo", "C::bar");
+
+ private final TestParameters parameters;
+
+ @Parameterized.Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimes().withAllApiLevels().build();
+ }
+
+ public MergedContextTest(TestParameters parameters) {
+ this.parameters = parameters;
+ }
+
+ @Test
+ public void test() throws Exception {
+ testForR8(parameters.getBackend())
+ .addProgramClasses(TestClass.class, A.class, B.class, C.class)
+ .addKeepClassAndMembersRules(TestClass.class)
+ .addKeepRules("-repackageclasses \"repackaged\"")
+ .enableInliningAnnotations()
+ .enableNoHorizontalClassMergingAnnotations()
+ .enableNeverClassInliningAnnotations()
+ .setMinApi(parameters.getApiLevel())
+ .addHorizontallyMergedClassesInspector(
+ inspector -> {
+ inspector.assertClassNotMerged(C.class);
+ inspector.assertMergedInto(B.class, A.class);
+ })
+ .run(parameters.getRuntime(), TestClass.class)
+ .assertSuccessWithOutput(EXPECTED);
+ }
+
+ /* This class will be merged with class B (with A being the result). This class has a package
+ * protected access to ensure that it cannot be repackaged. */
+ @NeverClassInline
+ public static class A {
+
+ @NeverInline
+ public void ensureNotRepackaged() {
+ TestClass.packageProtectedMethodToDisableRepackage();
+ }
+ }
+
+ /* This class is merged into A. */
+ @NeverClassInline
+ public static class B {
+
+ @NeverInline
+ public Runnable foo() {
+ C c = new C();
+ // This synthetic lambda class uses package protected access to C. Its context will initially
+ // be B, thus the synthetic will internally be B-$$Synthetic. The lambda can be repackaged
+ // together with the accessed class C. However, once A and B are merged as A, the context
+ // implicitly changes. If repackaging does not either see or adjust the context, the result
+ // will be that the external synthetic lambda will become A-$$Synthetic,
+ // with the consequence that the call to repackaged.C.protectedMethod() will throw IAE.
+ return () -> {
+ System.out.println("B::foo");
+ c.packageProtectedMethod();
+ };
+ }
+ }
+
+ @NeverClassInline
+ @NoHorizontalClassMerging
+ public static class C {
+
+ @NeverInline
+ void packageProtectedMethod() {
+ System.out.println("C::bar");
+ }
+ }
+
+ static class TestClass {
+
+ static void packageProtectedMethodToDisableRepackage() {
+ if (System.nanoTime() < 0) {
+ throw new RuntimeException();
+ }
+ }
+
+ public static void main(String[] args) {
+ new A().ensureNotRepackaged();
+ new B().foo().run();
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/FullNestOnProgramPathTest.java b/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/FullNestOnProgramPathTest.java
index e4447b53..8a37cd0 100644
--- a/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/FullNestOnProgramPathTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/FullNestOnProgramPathTest.java
@@ -50,7 +50,7 @@
return getTestParameters()
.withCfRuntimesStartingFromIncluding(CfVm.JDK11)
.withDexRuntimes()
- .withAllApiLevels()
+ .withApiLevelsStartingAtIncluding(apiLevelWithInvokeCustomSupport())
.build();
}
@@ -107,11 +107,7 @@
.addKeepAllAttributes()
.setMinApi(parameters.getApiLevel())
.addProgramFiles(classesOfNest(nestID))
- .addOptionsModification(
- options -> {
- options.enableNestBasedAccessDesugaring = true;
- options.enableNestReduction = false;
- })
+ .addOptionsModification(options -> options.enableNestReduction = false)
.compile()
.run(parameters.getRuntime(), getMainClass(nestID))
.assertSuccessWithOutput(getExpectedResult(nestID));
@@ -125,7 +121,6 @@
testForD8()
.setMinApi(parameters.getApiLevel())
.addProgramFiles(classesOfNest(nestID))
- .addOptionsModification(options -> options.enableNestBasedAccessDesugaring = true)
.compile()
.run(parameters.getRuntime(), getMainClass(nestID))
.assertSuccessWithOutput(getExpectedResult(nestID));
@@ -136,10 +131,6 @@
throws CompilationFailedException {
return testForD8(getStaticTemp())
.addProgramFiles(JAR)
- .addOptionsModification(
- options -> {
- options.enableNestBasedAccessDesugaring = true;
- })
.setMinApi(minApi)
.compile();
}
@@ -150,12 +141,9 @@
.noTreeShaking()
.noMinification()
.addKeepAllAttributes()
- .addOptionsModification(
- options -> {
- options.enableNestBasedAccessDesugaring = true;
- options.enableNestReduction = false;
- })
+ .addOptionsModification(options -> options.enableNestReduction = false)
.addProgramFiles(JAR)
+ .addInliningAnnotations()
.setMinApi(minApi)
.compile();
}
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 6ebe0db..bf9a226 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
@@ -5,7 +5,6 @@
package com.android.tools.r8.desugar.nestaccesscontrol;
import static junit.framework.TestCase.assertTrue;
-import static org.hamcrest.CoreMatchers.containsString;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
@@ -51,10 +50,13 @@
.setMinApi(parameters.getApiLevel())
.addProgramFiles(ToolHelper.R8_WITH_RELOCATED_DEPS_11_JAR)
.addKeepRuleFiles(MAIN_KEEP)
+ // TODO(b/177967938): Investigate why this is needed.
+ .applyIf(
+ parameters.getApiLevel().isLessThan(AndroidApiLevel.O),
+ builder -> builder.addDontWarnJavaLangInvoke().addDontWarnJavaNioFile(),
+ builder -> builder.addDontWarnJavaLangInvoke("StringConcatFactory"))
.addOptionsModification(opt -> opt.ignoreMissingClasses = true)
- .allowDiagnosticWarningMessages()
.compile()
- .assertAllWarningMessagesMatch(containsString("Missing class "))
.inspect(this::assertNotEmpty)
.inspect(Java11R8CompilationTest::assertNoNests);
}
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 9d01c91..c55df8c 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,7 @@
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.ir.desugar.NestBasedAccessDesugaring;
+import com.android.tools.r8.ir.desugar.nest.NestBasedAccessDesugaring;
import com.android.tools.r8.utils.codeinspector.ClassSubject;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
import com.android.tools.r8.utils.codeinspector.FoundMethodSubject;
@@ -37,7 +37,7 @@
.withCfRuntimesStartingFromIncluding(CfVm.JDK11)
.withDexRuntime(DexVm.Version.first())
.withDexRuntime(DexVm.Version.last())
- .withAllApiLevels()
+ .withApiLevelsStartingAtIncluding(apiLevelWithInvokeCustomSupport())
.build();
}
diff --git a/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/NestAttributesUpdateTest.java b/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/NestAttributesUpdateTest.java
index 816a233..aa22447 100644
--- a/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/NestAttributesUpdateTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/NestAttributesUpdateTest.java
@@ -94,6 +94,7 @@
options.enableClassInlining = false;
})
.addProgramFiles(classesMatching(outerNestName))
+ .addInliningAnnotations()
.compile()
.inspect(
inspector -> {
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 7f2fede..66e2ec7 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
@@ -4,12 +4,15 @@
package com.android.tools.r8.desugar.nestaccesscontrol;
+import static com.android.tools.r8.DiagnosticsMatcher.diagnosticType;
import static com.android.tools.r8.desugar.nestaccesscontrol.NestAccessControlTestUtils.CLASSES_PATH;
import static com.android.tools.r8.desugar.nestaccesscontrol.NestAccessControlTestUtils.CLASS_NAMES;
import static com.android.tools.r8.utils.FileUtils.CLASS_EXTENSION;
import static java.util.stream.Collectors.toList;
import static org.hamcrest.core.StringContains.containsString;
import static org.hamcrest.core.StringEndsWith.endsWith;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
@@ -20,9 +23,12 @@
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
import com.android.tools.r8.TestRuntime.CfVm;
+import com.android.tools.r8.ToolHelper;
import com.android.tools.r8.ToolHelper.DexVm;
import com.android.tools.r8.errors.IncompleteNestNestDesugarDiagnosic;
+import com.android.tools.r8.errors.InterfaceDesugarMissingTypeDiagnostic;
import com.android.tools.r8.errors.MissingNestHostNestDesugarDiagnostic;
+import com.android.tools.r8.shaking.MissingClassesDiagnostic;
import java.nio.file.Path;
import java.util.List;
import org.hamcrest.Matcher;
@@ -47,17 +53,19 @@
.withCfRuntimesStartingFromIncluding(CfVm.JDK11)
.withDexRuntime(DexVm.Version.first())
.withDexRuntime(DexVm.Version.last())
- .withAllApiLevels()
+ .withApiLevelsStartingAtIncluding(apiLevelWithInvokeCustomSupport())
.build();
}
@Test
- public void testWarningD8() throws Exception {
+ public void testD8() throws Exception {
Assume.assumeTrue(parameters.isDexRuntime());
- testIncompleteNestWarning(true, true);
- testMissingNestHostWarning(true, true);
+ testMissingNestHostError(true);
+ testIncompleteNestError(true);
}
-
+ // TODO R8:
+ // appView.options().reportMissingNestHost(clazz);
+ // clazz.clearNestHost();
@Test
public void testWarningR8() throws Exception {
testIncompleteNestWarning(false, parameters.isDexRuntime());
@@ -66,8 +74,8 @@
@Test
public void testErrorR8() {
- testMissingNestHostError();
- testIncompleteNestError();
+ testMissingNestHostError(false);
+ testIncompleteNestError(false);
}
private TestCompilerBuilder<?, ?, ?, ?, ?> compileOnlyClassesMatching(
@@ -81,10 +89,7 @@
.map(name -> CLASSES_PATH.resolve(name + CLASS_EXTENSION))
.collect(toList());
if (d8) {
- return testForD8()
- .setMinApi(parameters.getApiLevel())
- .addProgramFiles(matchingClasses)
- .addOptionsModification(options -> options.enableNestBasedAccessDesugaring = true);
+ return testForD8().setMinApi(parameters.getApiLevel()).addProgramFiles(matchingClasses);
} else {
return testForR8(parameters.getBackend())
.noTreeShaking()
@@ -92,23 +97,49 @@
.addKeepAllAttributes()
.setMinApi(parameters.getApiLevel())
.addProgramFiles(matchingClasses)
+ .addLibraryFiles(ToolHelper.getMostRecentAndroidJar())
+ .addDontWarn("java.lang.invoke.StringConcatFactory")
.addOptionsModification(
options -> {
- options.enableNestBasedAccessDesugaring = true;
options.ignoreMissingClasses = ignoreMissingClasses;
+ options.testing.enableExperimentalMissingClassesReporting = true;
})
.allowDiagnosticWarningMessages(allowDiagnosticWarningMessages);
}
}
- private void testMissingNestHostError() {
+ private void testMissingNestHostError(boolean d8) {
try {
Matcher<String> innerClassMatcher =
containsString("BasicNestHostWithInnerClassMethods$BasicNestedClass");
- compileOnlyClassesMatching(innerClassMatcher, false, false, false)
+ compileOnlyClassesMatching(innerClassMatcher, d8, false, false)
.compileWithExpectedDiagnostics(
diagnostics -> {
- diagnostics.assertErrorMessageThatMatches(containsString("requires its nest host"));
+ if (d8) {
+ diagnostics
+ .assertOnlyErrors()
+ .assertErrorsMatch(
+ diagnosticType(MissingNestHostNestDesugarDiagnostic.class));
+
+ MissingNestHostNestDesugarDiagnostic diagnostic =
+ (MissingNestHostNestDesugarDiagnostic) diagnostics.getErrors().get(0);
+ assertEquals(
+ "Class BasicNestHostWithInnerClassMethods$BasicNestedClass requires its nest "
+ + "host BasicNestHostWithInnerClassMethods to be on program or class "
+ + "path.",
+ diagnostic.getDiagnosticMessage());
+ } else {
+ diagnostics
+ .assertOnlyErrors()
+ .assertErrorsMatch(diagnosticType(MissingClassesDiagnostic.class));
+
+ MissingClassesDiagnostic diagnostic =
+ (MissingClassesDiagnostic) diagnostics.getErrors().get(0);
+ assertEquals(1, diagnostic.getMissingClasses().size());
+ assertEquals(
+ "nesthostexample.BasicNestHostWithInnerClassMethods",
+ diagnostic.getMissingClasses().iterator().next().getTypeName());
+ }
});
} catch (CompilationFailedException e) {
// Expected failure.
@@ -117,14 +148,37 @@
fail("Should have raised an exception for missing nest host");
}
- private void testIncompleteNestError() {
+ private void testIncompleteNestError(boolean d8) {
try {
Matcher<String> innerClassMatcher = endsWith("BasicNestHostWithInnerClassMethods");
- compileOnlyClassesMatching(innerClassMatcher, false, false, false)
+ compileOnlyClassesMatching(innerClassMatcher, d8, false, false)
.compileWithExpectedDiagnostics(
diagnostics -> {
- diagnostics.assertErrorMessageThatMatches(
- containsString("requires its nest mates"));
+ if (d8) {
+ diagnostics
+ .assertOnlyErrors()
+ .assertErrorsMatch(diagnosticType(IncompleteNestNestDesugarDiagnosic.class));
+
+ IncompleteNestNestDesugarDiagnosic diagnostic =
+ (IncompleteNestNestDesugarDiagnosic) diagnostics.getErrors().get(0);
+ assertEquals(
+ "Compilation of classes nesthostexample.BasicNestHostWithInnerClassMethods "
+ + "requires its nest mates "
+ + "nesthostexample.BasicNestHostWithInnerClassMethods$BasicNestedClass "
+ + "(unavailable) to be on program or class path.",
+ diagnostic.getDiagnosticMessage());
+ } else {
+ diagnostics
+ .assertOnlyErrors()
+ .assertErrorsMatch(diagnosticType(MissingClassesDiagnostic.class));
+
+ MissingClassesDiagnostic diagnostic =
+ (MissingClassesDiagnostic) diagnostics.getErrors().get(0);
+ assertEquals(1, diagnostic.getMissingClasses().size());
+ assertEquals(
+ "nesthostexample.BasicNestHostWithInnerClassMethods$BasicNestedClass",
+ diagnostic.getMissingClasses().iterator().next().getTypeName());
+ }
});
} catch (Exception e) {
// Expected failure.
@@ -139,15 +193,31 @@
TestCompileResult<?, ?> compileResult =
compileOnlyClassesMatching(innerClassMatcher, d8, !d8, true).compile();
assertTrue(compileResult.getDiagnosticMessages().getWarnings().size() >= 1);
- if (desugarWarning) {
+ if (d8 && desugarWarning) {
assertTrue(
compileResult.getDiagnosticMessages().getWarnings().stream()
.anyMatch(warn -> warn instanceof MissingNestHostNestDesugarDiagnostic));
- } else if (!d8) {
+ }
+ if (!d8) {
// R8 should raise extra warning when cleaning the nest.
- assertTrue(
- compileResult.getDiagnosticMessages().getWarnings().stream()
- .anyMatch(warn -> warn.getDiagnosticMessage().contains("requires its nest host")));
+ compileResult.inspectDiagnosticMessages(
+ diagnostics -> {
+ diagnostics.assertOnlyWarnings();
+ if (parameters.isCfRuntime() || parameters.canUseDefaultAndStaticInterfaceMethods()) {
+ diagnostics.assertWarningsMatch(diagnosticType(MissingClassesDiagnostic.class));
+ } else {
+ diagnostics.assertWarningsMatch(
+ diagnosticType(MissingClassesDiagnostic.class),
+ diagnosticType(InterfaceDesugarMissingTypeDiagnostic.class));
+ }
+
+ MissingClassesDiagnostic diagnostic =
+ (MissingClassesDiagnostic) diagnostics.getWarnings().get(0);
+ assertEquals(1, diagnostic.getMissingClasses().size());
+ assertEquals(
+ "nesthostexample.BasicNestHostWithInnerClassMethods",
+ diagnostic.getMissingClasses().iterator().next().getTypeName());
+ });
}
}
@@ -156,13 +226,32 @@
TestCompileResult<?, ?> compileResult =
compileOnlyClassesMatching(innerClassMatcher, d8, !d8, true).compile();
assertTrue(compileResult.getDiagnosticMessages().getWarnings().size() >= 1);
- if (desugarWarning) {
+ if (d8 && desugarWarning) {
assertTrue(
compileResult.getDiagnosticMessages().getWarnings().stream()
.anyMatch(warn -> warn instanceof IncompleteNestNestDesugarDiagnosic));
- } else if (!d8) {
+ }
+ if (!d8) {
// R8 should raise extra warning when cleaning the nest.
- compileResult.assertWarningMessageThatMatches(containsString("requires its nest mates"));
+ compileResult.inspectDiagnosticMessages(
+ diagnostics -> {
+ diagnostics.assertOnlyWarnings();
+ if (parameters.isCfRuntime() || parameters.canUseDefaultAndStaticInterfaceMethods()) {
+ diagnostics.assertWarningsMatch(diagnosticType(MissingClassesDiagnostic.class));
+ } else {
+ diagnostics.assertWarningsMatch(
+ diagnosticType(MissingClassesDiagnostic.class),
+ diagnosticType(InterfaceDesugarMissingTypeDiagnostic.class));
+ }
+
+ MissingClassesDiagnostic diagnostic =
+ (MissingClassesDiagnostic) diagnostics.getWarnings().get(0);
+ assertNotNull(diagnostic);
+ assertEquals(1, diagnostic.getMissingClasses().size());
+ assertEquals(
+ "nesthostexample.BasicNestHostWithInnerClassMethods$BasicNestedClass",
+ diagnostic.getMissingClasses().iterator().next().getTypeName());
+ });
}
}
}
diff --git a/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/NestConstructorRemovedArgTest.java b/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/NestConstructorRemovedArgTest.java
index 1ed0e0a..85bade9 100644
--- a/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/NestConstructorRemovedArgTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/NestConstructorRemovedArgTest.java
@@ -33,7 +33,7 @@
.withCfRuntimesStartingFromIncluding(CfVm.JDK11)
.withDexRuntime(DexVm.Version.first())
.withDexRuntime(DexVm.Version.last())
- .withAllApiLevels()
+ .withApiLevelsStartingAtIncluding(apiLevelWithInvokeCustomSupport())
.build();
}
diff --git a/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/NestOnProgramAndClassPathTest.java b/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/NestOnProgramAndClassPathTest.java
index bb59533..4029469 100644
--- a/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/NestOnProgramAndClassPathTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/NestOnProgramAndClassPathTest.java
@@ -99,9 +99,7 @@
testForD8()
.addProgramFiles(inner.writeToZip(), host.writeToZip())
.setMinApi(parameters.getApiLevel())
- .addOptionsModification(options -> options.enableNestBasedAccessDesugaring = true)
.compile()
- .inspect(inspector -> assertEquals(3, inspector.allClasses().size()))
.run(parameters.getRuntime(), getMainClass("constructors"))
.assertSuccessWithOutput(getExpectedResult("constructors"));
}
@@ -117,7 +115,6 @@
.setMinApi(parameters.getApiLevel())
.addProgramFiles(matchingClasses)
.addClasspathFiles(JAR)
- .addOptionsModification(options -> options.enableNestBasedAccessDesugaring = true)
.compile();
}
diff --git a/src/test/java/com/android/tools/r8/desugar/staticinterfacemethod/MissingMethodTest.java b/src/test/java/com/android/tools/r8/desugar/staticinterfacemethod/MissingMethodTest.java
index f147d3b..d36953c 100644
--- a/src/test/java/com/android/tools/r8/desugar/staticinterfacemethod/MissingMethodTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/staticinterfacemethod/MissingMethodTest.java
@@ -6,7 +6,6 @@
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
-import com.android.tools.r8.TestRunResult;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
@@ -28,33 +27,19 @@
@Test
public void testReference() throws Exception {
- TestRunResult<?> result =
- testForRuntime(parameters)
- .addProgramClassFileData(InterfaceDump.dump(), MainDump.dump())
- .run(parameters.getRuntime(), "Main");
- if (parameters.isDexRuntime()
- && parameters.getApiLevel().isLessThan(apiLevelWithStaticInterfaceMethodsSupport())) {
- // TODO(b/69835274): Desugaring should preserve exception.
- result.assertFailureWithErrorThatThrows(NoClassDefFoundError.class);
- } else {
- result.assertFailureWithErrorThatThrows(NoSuchMethodError.class);
- }
+ testForRuntime(parameters)
+ .addProgramClassFileData(InterfaceDump.dump(), MainDump.dump())
+ .run(parameters.getRuntime(), "Main")
+ .assertFailureWithErrorThatThrows(NoSuchMethodError.class);
}
@Test
public void testR8() throws Exception {
- TestRunResult<?> result =
- testForR8(parameters.getBackend())
- .addProgramClassFileData(InterfaceDump.dump(), MainDump.dump())
- .addKeepMainRule("Main")
- .setMinApi(parameters.getApiLevel())
- .run(parameters.getRuntime(), "Main");
- if (parameters.isDexRuntime()
- && parameters.getApiLevel().isLessThan(apiLevelWithStaticInterfaceMethodsSupport())) {
- // TODO(b/69835274): Desugaring should preserve exception.
- result.assertFailureWithErrorThatThrows(NoClassDefFoundError.class);
- } else {
- result.assertFailureWithErrorThatThrows(NoSuchMethodError.class);
- }
+ testForR8(parameters.getBackend())
+ .addProgramClassFileData(InterfaceDump.dump(), MainDump.dump())
+ .addKeepMainRule("Main")
+ .setMinApi(parameters.getApiLevel())
+ .run(parameters.getRuntime(), "Main")
+ .assertFailureWithErrorThatThrows(NoSuchMethodError.class);
}
}
diff --git a/src/test/java/com/android/tools/r8/desugar/twr/TwrCloseResourceDuplicationTest.java b/src/test/java/com/android/tools/r8/desugar/twr/TwrCloseResourceDuplicationTest.java
new file mode 100644
index 0000000..aac4a9e
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/desugar/twr/TwrCloseResourceDuplicationTest.java
@@ -0,0 +1,160 @@
+// 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.twr;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assume.assumeTrue;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.utils.StringUtils;
+import com.android.tools.r8.utils.ZipUtils;
+import com.android.tools.r8.utils.codeinspector.FoundClassSubject;
+import java.io.IOException;
+import java.util.List;
+import java.util.jar.JarFile;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class TwrCloseResourceDuplicationTest extends TestBase {
+
+ static final int INPUT_CLASSES = 3;
+
+ static final String EXPECTED =
+ StringUtils.lines(
+ "foo opened 1",
+ "foo post close 1",
+ "foo opened 2",
+ "foo caught from 2: RuntimeException",
+ "foo post close 2",
+ "bar opened 1",
+ "bar post close 1",
+ "bar opened 2",
+ "bar caught from 2: RuntimeException",
+ "bar post close 2");
+
+ private final TestParameters parameters;
+
+ @Parameterized.Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimes().withAllApiLevelsAlsoForCf().build();
+ }
+
+ public TwrCloseResourceDuplicationTest(TestParameters parameters) {
+ this.parameters = parameters;
+ }
+
+ private String getZipFile() throws IOException {
+ return ZipUtils.ZipBuilder.builder(temp.newFile("file.zip").toPath())
+ // DEX VMs from 4.4 up-to 9.0 including, will fail if no entry is added.
+ .addBytes("entry", new byte[1])
+ .build()
+ .toString();
+ }
+
+ @Test
+ public void testJvm() throws Exception {
+ assumeTrue(parameters.isCfRuntime());
+ testForJvm()
+ .addInnerClasses(getClass())
+ .run(parameters.getRuntime(), TestClass.class, getZipFile())
+ .assertSuccessWithOutput(EXPECTED);
+ }
+
+ @Test
+ public void testD8() throws Exception {
+ testForD8(parameters.getBackend())
+ .addInnerClasses(getClass())
+ .setMinApi(parameters.getApiLevel())
+ .run(parameters.getRuntime(), TestClass.class, getZipFile())
+ .assertSuccessWithOutput(EXPECTED)
+ .inspect(
+ inspector -> {
+ // There should be exactly one synthetic class besides the three program classes.
+ int expectedSynthetics =
+ parameters.getApiLevel().isLessThan(apiLevelWithTwrCloseResourceSupport())
+ ? 1
+ : 0;
+ assertEquals(INPUT_CLASSES + expectedSynthetics, inspector.allClasses().size());
+ });
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ assumeTrue(parameters.isDexRuntime());
+ testForR8(parameters.getBackend())
+ .addInnerClasses(getClass())
+ .addKeepMainRule(TestClass.class)
+ .addKeepClassAndMembersRules(Foo.class, Bar.class)
+ .applyIf(
+ parameters.getApiLevel().isLessThan(apiLevelWithTwrCloseResourceSupport()),
+ builder -> builder.addDontWarn("java.lang.AutoCloseable"))
+ .setMinApi(parameters.getApiLevel())
+ .noMinification()
+ .run(parameters.getRuntime(), TestClass.class, getZipFile())
+ .assertSuccessWithOutput(EXPECTED)
+ .inspect(
+ inspector -> {
+ // R8 will optimize the generated methods for the two cases below where the thrown
+ // exception is known or not, thus the synthetic methods will be 2.
+ int expectedSynthetics =
+ parameters.getApiLevel().isLessThan(apiLevelWithTwrCloseResourceSupport())
+ ? 2
+ : 0;
+ List<FoundClassSubject> foundClassSubjects = inspector.allClasses();
+ assertEquals(INPUT_CLASSES + expectedSynthetics, foundClassSubjects.size());
+ });
+ }
+
+ static class Foo {
+ void foo(String name) {
+ try (JarFile f = new JarFile(name)) {
+ System.out.println("foo opened 1");
+ } catch (Exception e) {
+ System.out.println("foo caught from 1: " + e.getClass().getSimpleName());
+ } finally {
+ System.out.println("foo post close 1");
+ }
+ try (JarFile f = new JarFile(name)) {
+ System.out.println("foo opened 2");
+ throw new RuntimeException();
+ } catch (Exception e) {
+ System.out.println("foo caught from 2: " + e.getClass().getSimpleName());
+ } finally {
+ System.out.println("foo post close 2");
+ }
+ }
+ }
+
+ static class Bar {
+ void bar(String name) {
+ try (JarFile f = new JarFile(name)) {
+ System.out.println("bar opened 1");
+ } catch (Exception e) {
+ System.out.println("bar caught from 1: " + e.getClass().getSimpleName());
+ } finally {
+ System.out.println("bar post close 1");
+ }
+ try (JarFile f = new JarFile(name)) {
+ System.out.println("bar opened 2");
+ throw new RuntimeException();
+ } catch (Exception e) {
+ System.out.println("bar caught from 2: " + e.getClass().getSimpleName());
+ } finally {
+ System.out.println("bar post close 2");
+ }
+ }
+ }
+
+ static class TestClass {
+
+ public static void main(String[] args) {
+ new Foo().foo(args[0]);
+ new Bar().bar(args[0]);
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/desugaring/lambdanames/PackageDependentLambdaNamesTest.java b/src/test/java/com/android/tools/r8/desugaring/lambdanames/PackageDependentLambdaNamesTest.java
index 934a15b..e76e476 100644
--- a/src/test/java/com/android/tools/r8/desugaring/lambdanames/PackageDependentLambdaNamesTest.java
+++ b/src/test/java/com/android/tools/r8/desugaring/lambdanames/PackageDependentLambdaNamesTest.java
@@ -12,6 +12,7 @@
import com.android.tools.r8.utils.BooleanUtils;
import com.android.tools.r8.utils.DescriptorUtils;
import com.android.tools.r8.utils.StringUtils;
+import com.android.tools.r8.utils.codeinspector.FoundClassSubject;
import java.util.List;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -47,11 +48,11 @@
if (parameters.isDexRuntime()) {
result.inspect(
inspector -> {
- // When in the same package we expect the two System.out::print lambdas to be shared.
+ // With the hygienic synthetics the reference to System.out::print can always be shared.
assertEquals(
- samePackage ? 2 : 3,
+ 2,
inspector.allClasses().stream()
- .filter(c -> c.isSynthesizedJavaLambdaClass())
+ .filter(FoundClassSubject::isSynthesizedJavaLambdaClass)
.count());
});
}
@@ -85,7 +86,6 @@
return transformer.transform();
}
- @FunctionalInterface
public interface StringConsumer {
void accept(String arg);
}
diff --git a/src/test/java/com/android/tools/r8/dexsplitter/DexSplitterMergeRegression.java b/src/test/java/com/android/tools/r8/dexsplitter/DexSplitterMergeRegression.java
index c9dd274..780696e 100644
--- a/src/test/java/com/android/tools/r8/dexsplitter/DexSplitterMergeRegression.java
+++ b/src/test/java/com/android/tools/r8/dexsplitter/DexSplitterMergeRegression.java
@@ -62,8 +62,7 @@
r8FullTestBuilder
.enableNoVerticalClassMergingAnnotations()
.enableInliningAnnotations()
- .noMinification()
- .addOptionsModification(o -> o.testing.deterministicSortingBasedOnDexType = true);
+ .noMinification();
ProcessResult processResult =
testDexSplitter(
parameters,
diff --git a/src/test/java/com/android/tools/r8/dexsplitter/SplitterTestBase.java b/src/test/java/com/android/tools/r8/dexsplitter/SplitterTestBase.java
index c41a1d1..dce9fbc 100644
--- a/src/test/java/com/android/tools/r8/dexsplitter/SplitterTestBase.java
+++ b/src/test/java/com/android/tools/r8/dexsplitter/SplitterTestBase.java
@@ -143,6 +143,7 @@
.addProgramClasses(baseClasses)
.addFeatureSplit(
builder -> simpleSplitProvider(builder, featureOutput, temp, featureClasses))
+ .addInliningAnnotations()
.setMinApi(parameters.getApiLevel())
.addKeepMainRule(SplitRunner.class)
.addKeepClassRules(toRun);
@@ -182,6 +183,7 @@
.addClasspathClasses(baseClasses)
.addClasspathClasses(RunInterface.class)
.addKeepAllClassesRule()
+ .addInliningAnnotations()
.setMinApi(parameters.getApiLevel())
.compile()
.writeToZip();
diff --git a/src/test/java/com/android/tools/r8/diagnostics/ErrorDuringIrConversionTest.java b/src/test/java/com/android/tools/r8/diagnostics/ErrorDuringIrConversionTest.java
index b9eabe6..89088ac 100644
--- a/src/test/java/com/android/tools/r8/diagnostics/ErrorDuringIrConversionTest.java
+++ b/src/test/java/com/android/tools/r8/diagnostics/ErrorDuringIrConversionTest.java
@@ -86,7 +86,8 @@
try {
testForD8()
.apply(addTestClassWithOrigin())
- .addOptionsModification(options -> options.testing.hookInIrConversion = () -> throwNPE())
+ .addOptionsModification(
+ options -> options.testing.hookInIrConversion = ErrorDuringIrConversionTest::throwNPE)
.compileWithExpectedDiagnostics(
diagnostics -> {
// Check that the error is reported as an error to the diagnostics handler.
diff --git a/src/test/java/com/android/tools/r8/diagnostics/ModifyDiagnosticsLevelTest.java b/src/test/java/com/android/tools/r8/diagnostics/ModifyDiagnosticsLevelTest.java
index 3dabac7..4b1bb9e 100644
--- a/src/test/java/com/android/tools/r8/diagnostics/ModifyDiagnosticsLevelTest.java
+++ b/src/test/java/com/android/tools/r8/diagnostics/ModifyDiagnosticsLevelTest.java
@@ -48,12 +48,11 @@
})
.allowDiagnosticInfoMessages()
.compileWithExpectedDiagnostics(
- diagnostics -> {
- diagnostics
- .assertOnlyInfos()
- .assertInfosCount(1)
- .assertInfosMatch(diagnosticMessage(startsWith(MISSING_CLASS_MESSAGE_PREFIX)));
- });
+ diagnostics ->
+ diagnostics
+ .assertOnlyInfos()
+ .assertInfosCount(1)
+ .assertInfosMatch(diagnosticMessage(startsWith(MISSING_CLASS_MESSAGE_PREFIX))));
}
@Test
diff --git a/src/test/java/com/android/tools/r8/enumunboxing/AnnotationEnumUnboxingTest.java b/src/test/java/com/android/tools/r8/enumunboxing/AnnotationEnumUnboxingTest.java
index 20362c3..0bc6b67 100644
--- a/src/test/java/com/android/tools/r8/enumunboxing/AnnotationEnumUnboxingTest.java
+++ b/src/test/java/com/android/tools/r8/enumunboxing/AnnotationEnumUnboxingTest.java
@@ -51,6 +51,7 @@
.addKeepRuntimeVisibleAnnotations()
.enableNeverClassInliningAnnotations()
.enableInliningAnnotations()
+ .enableMemberValuePropagationAnnotations()
.enableNoVerticalClassMergingAnnotations()
.addOptionsModification(opt -> enableEnumOptions(opt, enumValueOptimization))
.allowDiagnosticInfoMessages()
diff --git a/src/test/java/com/android/tools/r8/enumunboxing/EnumToStringLibTest.java b/src/test/java/com/android/tools/r8/enumunboxing/EnumToStringLibTest.java
index 89395ef..b21cbd0 100644
--- a/src/test/java/com/android/tools/r8/enumunboxing/EnumToStringLibTest.java
+++ b/src/test/java/com/android/tools/r8/enumunboxing/EnumToStringLibTest.java
@@ -97,13 +97,6 @@
return testForR8(Backend.CF)
.addProgramClasses(ToStringLib.class, ToStringLib.LibEnum.class)
.addKeepRules(enumKeepRules.getKeepRules())
- // TODO(b/160535629): Work-around on some optimizations relying on $VALUES name.
- .addKeepRules(
- "-keep enum "
- + ToStringLib.LibEnum.class.getName()
- + " { static "
- + ToStringLib.LibEnum.class.getName()
- + "[] $VALUES; }")
.addOptionsModification(opt -> enableEnumOptions(opt, enumValueOptimization))
.addKeepMethodRules(
Reference.methodFromMethod(
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 a4f280c..3c48865 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
@@ -4,8 +4,10 @@
package com.android.tools.r8.enumunboxing.kotlin;
+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;
@@ -68,18 +70,25 @@
public void testEnumUnboxing() throws Exception {
assumeTrue(parameters.isDexRuntime());
testForR8(parameters.getBackend())
- .addProgramFiles(jars.getForConfiguration(kotlinCompiler, targetVersion))
+ .addProgramFiles(
+ jars.getForConfiguration(kotlinCompiler, targetVersion),
+ ToolHelper.getKotlinStdlibJar(kotlinCompiler))
.addKeepMainRule(PKG + ".MainKt")
.addKeepRules(enumKeepRules.getKeepRules())
.addKeepRuntimeVisibleAnnotations()
.addOptionsModification(opt -> enableEnumOptions(opt, enumValueOptimization))
- .allowDiagnosticInfoMessages()
+ .allowDiagnosticMessages()
.setMinApi(parameters.getApiLevel())
.compile()
.inspectDiagnosticMessages(
- m -> {
+ messages -> {
+ messages
+ .assertNoErrors()
+ .assertAllWarningsMatch(
+ diagnosticMessage(
+ containsString("Resource 'META-INF/MANIFEST.MF' already exists.")));
assertEnumIsUnboxed(
- PKG + ".Color", SimpleKotlinEnumUnboxingTest.class.getSimpleName(), m);
+ PKG + ".Color", SimpleKotlinEnumUnboxingTest.class.getSimpleName(), messages);
})
.run(parameters.getRuntime(), PKG + ".MainKt")
.assertSuccessWithOutputLines("RED", "GREEN", "BLUE");
diff --git a/src/test/java/com/android/tools/r8/graph/DexTypeTest.java b/src/test/java/com/android/tools/r8/graph/DexTypeTest.java
index 82ee9e8..83164c5 100644
--- a/src/test/java/com/android/tools/r8/graph/DexTypeTest.java
+++ b/src/test/java/com/android/tools/r8/graph/DexTypeTest.java
@@ -9,9 +9,11 @@
import com.android.tools.r8.ToolHelper;
import com.android.tools.r8.dex.ApplicationReader;
+import com.android.tools.r8.ir.analysis.type.InterfaceCollection;
import com.android.tools.r8.utils.AndroidApp;
import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.Timing;
+import java.util.HashSet;
import java.util.Set;
import org.junit.BeforeClass;
import org.junit.Test;
@@ -53,7 +55,7 @@
// class ArrayList implements List
DexType arrayList = factory.createType("Ljava/util/ArrayList;");
- Set<DexType> interfaces = appInfo.implementedInterfaces(arrayList);
+ Set<DexType> interfaces = setOfAll(appInfo.implementedInterfaces(arrayList));
assertThat(interfaces, hasItems(serializable));
assertThat(interfaces, hasItems(iterable));
assertThat(interfaces, hasItems(collection));
@@ -62,7 +64,7 @@
// class LinkedList implements List, Deque
DexType linkedList = factory.createType("Ljava/util/LinkedList;");
- interfaces = appInfo.implementedInterfaces(linkedList);
+ interfaces = setOfAll(appInfo.implementedInterfaces(linkedList));
assertThat(interfaces, hasItems(serializable));
assertThat(interfaces, hasItems(iterable));
assertThat(interfaces, hasItems(collection));
@@ -71,6 +73,12 @@
assertThat(interfaces, hasItems(queue));
}
+ private static Set<DexType> setOfAll(InterfaceCollection interfaces) {
+ HashSet<DexType> set = new HashSet<>(interfaces.size());
+ interfaces.forEach((iface, ignoredIsKnown) -> set.add(iface));
+ return set;
+ }
+
@Test
public void implementedInterfaces_collections_kotlin() {
DexType iterable = factory.createType("Ljava/lang/Iterable;");
@@ -84,13 +92,13 @@
DexType ktAbsList = factory.createType("Lkotlin/collections/AbstractList;");
DexType ktAbsSet = factory.createType("Lkotlin/collections/AbstractSet;");
- Set<DexType> interfaces = appInfo.implementedInterfaces(ktAbsList);
+ Set<DexType> interfaces = setOfAll(appInfo.implementedInterfaces(ktAbsList));
assertThat(interfaces, hasItems(iterable));
assertThat(interfaces, hasItems(collection));
assertThat(interfaces, hasItems(list));
assertThat(interfaces, not(hasItems(set)));
- interfaces = appInfo.implementedInterfaces(ktAbsSet);
+ interfaces = setOfAll(appInfo.implementedInterfaces(ktAbsSet));
assertThat(interfaces, hasItems(iterable));
assertThat(interfaces, hasItems(collection));
assertThat(interfaces, hasItems(set));
@@ -107,7 +115,7 @@
DexType pType = factory.createType("Ljava/lang/reflect/ParameterizedType;");
DexType klass = factory.createType("Ljava/lang/Class;");
- Set<DexType> interfaces = appInfo.implementedInterfaces(klass);
+ Set<DexType> interfaces = setOfAll(appInfo.implementedInterfaces(klass));
assertThat(interfaces, hasItems(serializable));
assertThat(interfaces, hasItems(annotatedElement));
assertThat(interfaces, hasItems(genericDeclaration));
@@ -130,7 +138,7 @@
DexType mutableReference0 =
factory.createType("Lkotlin/jvm/internal/MutablePropertyReference0;");
- Set<DexType> interfaces = appInfo.implementedInterfaces(mutableReference0);
+ Set<DexType> interfaces = setOfAll(appInfo.implementedInterfaces(mutableReference0));
assertThat(interfaces, hasItems(kCallable));
assertThat(interfaces, hasItems(kProperty));
assertThat(interfaces, hasItems(kMutableProperty));
@@ -147,15 +155,14 @@
// interface Function0 : Function
DexType function0 = factory.createType("Lkotlin/jvm/functions/Function0;");
- Set<DexType> interfaces = appInfo.implementedInterfaces(lambda);
+ Set<DexType> interfaces = setOfAll(appInfo.implementedInterfaces(lambda));
assertThat(interfaces, not(hasItems(lambda)));
assertThat(interfaces, hasItems(function));
assertThat(interfaces, hasItems(functionBase));
- interfaces = appInfo.implementedInterfaces(function0);
+ interfaces = setOfAll(appInfo.implementedInterfaces(function0));
assertThat(interfaces, hasItems(function0));
assertThat(interfaces, hasItems(function));
assertThat(interfaces, not(hasItems(functionBase)));
}
-
}
diff --git a/src/test/java/com/android/tools/r8/graph/MissingClassThrowingTest.java b/src/test/java/com/android/tools/r8/graph/MissingClassThrowingTest.java
index 213db97..7277139 100644
--- a/src/test/java/com/android/tools/r8/graph/MissingClassThrowingTest.java
+++ b/src/test/java/com/android/tools/r8/graph/MissingClassThrowingTest.java
@@ -65,7 +65,7 @@
}
@Test
- public void testSuperTypeOfExceptions() throws Exception {
+ public void testSuperTypeOfExceptions() {
AssertUtils.assertFailsCompilation(
() ->
testForR8(parameters.getBackend())
diff --git a/src/test/java/com/android/tools/r8/graph/invokespecial/InvokeSpecialOnOtherInterfaceTest.java b/src/test/java/com/android/tools/r8/graph/invokespecial/InvokeSpecialOnOtherInterfaceTest.java
index 183ae6b..509bfa2 100644
--- a/src/test/java/com/android/tools/r8/graph/invokespecial/InvokeSpecialOnOtherInterfaceTest.java
+++ b/src/test/java/com/android/tools/r8/graph/invokespecial/InvokeSpecialOnOtherInterfaceTest.java
@@ -34,20 +34,20 @@
@Test
public void testRuntime() throws Exception {
testForRuntime(parameters.getRuntime(), parameters.getApiLevel())
- .addProgramClasses(I.class, Main.class)
+ .addProgramClassesAndInnerClasses(Main.class)
+ .addProgramClasses(I.class)
.addProgramClassFileData(getClassWithTransformedInvoked())
.run(parameters.getRuntime(), Main.class)
- .assertFailureWithErrorThatThrows(
- parameters.isCfRuntime()
- ? VerifyError.class
- // TODO(b/144410139): Consider making this a compilation failure.
- : NoClassDefFoundError.class);
+ // TODO(b/144410139): Consider making this a compilation failure when generating DEX.
+ .assertSuccessWithOutputLinesIf(parameters.isDexRuntime(), "Hello World!")
+ .assertFailureWithErrorThatThrowsIf(parameters.isCfRuntime(), VerifyError.class);
}
@Test
public void testR8() throws Exception {
testForR8(parameters.getBackend())
- .addProgramClasses(I.class, Main.class)
+ .addProgramClassesAndInnerClasses(Main.class)
+ .addProgramClasses(I.class)
.addProgramClassFileData(getClassWithTransformedInvoked())
.addKeepMainRule(Main.class)
.setMinApi(parameters.getApiLevel())
@@ -56,7 +56,7 @@
parameters.isCfRuntime()
? VerifyError.class
// TODO(b/144410139): Consider making this a compilation failure.
- : NoClassDefFoundError.class);
+ : NullPointerException.class);
}
private byte[] getClassWithTransformedInvoked() throws IOException {
diff --git a/src/test/java/com/android/tools/r8/graph/invokespecial/InvokeSpecialToSubclassTest.java b/src/test/java/com/android/tools/r8/graph/invokespecial/InvokeSpecialToSubclassTest.java
index b439787..9c44e2a 100644
--- a/src/test/java/com/android/tools/r8/graph/invokespecial/InvokeSpecialToSubclassTest.java
+++ b/src/test/java/com/android/tools/r8/graph/invokespecial/InvokeSpecialToSubclassTest.java
@@ -42,25 +42,21 @@
@Test
public void testRuntime() throws Exception {
testForRuntime(parameters.getRuntime(), parameters.getApiLevel())
- .addProgramClasses(EmptySubC.class, C.class, Main.class)
+ .addProgramClasses(EmptySubC.class, C.class, D.class, Main.class)
.addProgramClassFileData(getClassBWithTransformedInvoked(holder))
.run(parameters.getRuntime(), Main.class)
- // The failures are very different from one back-end to another: verification error,
- // invalid invoke-super, segmentation fault, NoSuchMethod, etc.
- .assertFailure();
+ .assertSuccessWithOutputLines("Should not be called.");
}
@Test
public void testR8() throws Exception {
testForR8(parameters.getBackend())
- .addProgramClasses(EmptySubC.class, C.class, Main.class)
+ .addProgramClasses(EmptySubC.class, C.class, D.class, Main.class)
.addProgramClassFileData(getClassBWithTransformedInvoked(holder))
.addKeepMainRule(Main.class)
.setMinApi(parameters.getApiLevel())
.run(parameters.getRuntime(), Main.class)
- // The failures are very different from one back-end to another: verification error,
- // invalid invoke-super, segmentation fault, NoSuchMethod, etc.
- .assertFailure();
+ .assertSuccessWithOutputLines("Should not be called.");
}
private byte[] getClassBWithTransformedInvoked(Class<?> holder) throws IOException {
diff --git a/src/test/java/com/android/tools/r8/internal/proto/ChromeProtoRewritingTest.java b/src/test/java/com/android/tools/r8/internal/proto/ChromeProtoRewritingTest.java
index 318de07..12236fc 100644
--- a/src/test/java/com/android/tools/r8/internal/proto/ChromeProtoRewritingTest.java
+++ b/src/test/java/com/android/tools/r8/internal/proto/ChromeProtoRewritingTest.java
@@ -44,6 +44,8 @@
keepAllProtosRule(),
keepDynamicMethodSignatureRule(),
keepNewMessageInfoSignatureRule())
+ .addDontWarn("android.content.pm.IPackageManager")
+ .allowUnusedDontWarnPatterns()
.allowUnusedProguardConfigurationRules()
.enableProtoShrinking(false)
.setMinApi(AndroidApiLevel.N)
diff --git a/src/test/java/com/android/tools/r8/internal/proto/Proto2BuilderOnlyReferencedFromDynamicMethodTest.java b/src/test/java/com/android/tools/r8/internal/proto/Proto2BuilderOnlyReferencedFromDynamicMethodTest.java
index 6405047..d603cc1 100644
--- a/src/test/java/com/android/tools/r8/internal/proto/Proto2BuilderOnlyReferencedFromDynamicMethodTest.java
+++ b/src/test/java/com/android/tools/r8/internal/proto/Proto2BuilderOnlyReferencedFromDynamicMethodTest.java
@@ -49,6 +49,7 @@
.addKeepRuleFiles(PROTOBUF_LITE_PROGUARD_RULES)
.allowAccessModification()
.allowDiagnosticMessages()
+ .allowUnusedDontWarnPatterns()
.allowUnusedProguardConfigurationRules()
.enableProtoShrinking()
.setMinApi(parameters.getApiLevel())
diff --git a/src/test/java/com/android/tools/r8/internal/proto/Proto2BuilderShrinkingTest.java b/src/test/java/com/android/tools/r8/internal/proto/Proto2BuilderShrinkingTest.java
index 1375f54..868c061 100644
--- a/src/test/java/com/android/tools/r8/internal/proto/Proto2BuilderShrinkingTest.java
+++ b/src/test/java/com/android/tools/r8/internal/proto/Proto2BuilderShrinkingTest.java
@@ -74,6 +74,7 @@
.addKeepRuleFiles(PROTOBUF_LITE_PROGUARD_RULES)
.allowAccessModification()
.allowDiagnosticMessages()
+ .allowUnusedDontWarnPatterns()
.allowUnusedProguardConfigurationRules()
.enableInliningAnnotations()
.enableProtoShrinking()
diff --git a/src/test/java/com/android/tools/r8/internal/proto/Proto2ShrinkingTest.java b/src/test/java/com/android/tools/r8/internal/proto/Proto2ShrinkingTest.java
index d88e281..2d2e79f 100644
--- a/src/test/java/com/android/tools/r8/internal/proto/Proto2ShrinkingTest.java
+++ b/src/test/java/com/android/tools/r8/internal/proto/Proto2ShrinkingTest.java
@@ -87,6 +87,7 @@
.addNoHorizontalClassMergingRule(PARTIALLY_USED + "$Enum$1")
.allowAccessModification(allowAccessModification)
.allowDiagnosticMessages()
+ .allowUnusedDontWarnPatterns()
.allowUnusedProguardConfigurationRules()
.enableProguardTestOptions()
.enableProtoShrinking()
@@ -356,6 +357,7 @@
.addKeepRules(keepDynamicMethodSignatureRule(), keepNewMessageInfoSignatureRule())
.allowAccessModification(allowAccessModification)
.allowDiagnosticMessages()
+ .allowUnusedDontWarnPatterns()
.allowUnusedProguardConfigurationRules()
.enableProtoShrinking()
.minification(enableMinification)
diff --git a/src/test/java/com/android/tools/r8/internal/proto/Proto3ShrinkingTest.java b/src/test/java/com/android/tools/r8/internal/proto/Proto3ShrinkingTest.java
index 429c340..a3c0705 100644
--- a/src/test/java/com/android/tools/r8/internal/proto/Proto3ShrinkingTest.java
+++ b/src/test/java/com/android/tools/r8/internal/proto/Proto3ShrinkingTest.java
@@ -60,6 +60,7 @@
.addKeepRuleFiles(PROTOBUF_LITE_PROGUARD_RULES)
.allowAccessModification(allowAccessModification)
.allowDiagnosticMessages()
+ .allowUnusedDontWarnPatterns()
.allowUnusedProguardConfigurationRules()
.enableProtoShrinking()
.minification(enableMinification)
@@ -108,6 +109,7 @@
.addKeepRules(keepDynamicMethodSignatureRule(), keepNewMessageInfoSignatureRule())
.allowAccessModification(allowAccessModification)
.allowDiagnosticMessages()
+ .allowUnusedDontWarnPatterns()
.allowUnusedProguardConfigurationRules()
.enableProtoShrinking()
.minification(enableMinification)
diff --git a/src/test/java/com/android/tools/r8/internal/proto/YouTubeV1508ProtoRewritingTest.java b/src/test/java/com/android/tools/r8/internal/proto/YouTubeV1508ProtoRewritingTest.java
index 5596fc7..7cdab46 100644
--- a/src/test/java/com/android/tools/r8/internal/proto/YouTubeV1508ProtoRewritingTest.java
+++ b/src/test/java/com/android/tools/r8/internal/proto/YouTubeV1508ProtoRewritingTest.java
@@ -31,6 +31,7 @@
public YouTubeV1508ProtoRewritingTest(TestParameters parameters) {
super(15, 8);
+ parameters.assertNoneRuntime();
}
@Test
@@ -55,6 +56,7 @@
.addMainDexRuleFiles(getMainDexRuleFiles())
.allowCheckDiscardedErrors(true)
.allowDiagnosticMessages()
+ .allowUnusedDontWarnPatterns()
.allowUnusedProguardConfigurationRules()
.setMinApi(AndroidApiLevel.H_MR2)
.compile()
diff --git a/src/test/java/com/android/tools/r8/ir/InlineTest.java b/src/test/java/com/android/tools/r8/ir/InlineTest.java
index cb48232..18f15fd 100644
--- a/src/test/java/com/android/tools/r8/ir/InlineTest.java
+++ b/src/test/java/com/android/tools/r8/ir/InlineTest.java
@@ -21,7 +21,6 @@
import com.android.tools.r8.ir.code.InstructionListIterator;
import com.android.tools.r8.shaking.Enqueuer;
import com.android.tools.r8.shaking.EnqueuerFactory;
-import com.android.tools.r8.shaking.ProguardClassFilter;
import com.android.tools.r8.shaking.ProguardConfiguration;
import com.android.tools.r8.shaking.ProguardKeepRule;
import com.android.tools.r8.shaking.RootSetBuilder;
@@ -75,10 +74,9 @@
ImmutableList.of(ProguardKeepRule.defaultKeepAllRule(unused -> {})))
.run(executorService));
Timing timing = Timing.empty();
- Enqueuer enqueuer = EnqueuerFactory.createForInitialTreeShaking(appView, subtypingInfo);
- appView.setAppInfo(
- enqueuer.traceApplication(
- appView.rootSet(), ProguardClassFilter.empty(), executorService, timing));
+ Enqueuer enqueuer =
+ EnqueuerFactory.createForInitialTreeShaking(appView, executorService, subtypingInfo);
+ appView.setAppInfo(enqueuer.traceApplication(appView.rootSet(), executorService, timing));
return new TestApplication(appView, method, additionalCode);
}
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 a2fe521..553a2f4 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
@@ -36,7 +36,6 @@
import it.unimi.dsi.fastutil.objects.Reference2IntMap;
import it.unimi.dsi.fastutil.objects.Reference2IntOpenHashMap;
import java.io.IOException;
-import java.util.concurrent.ExecutionException;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
@@ -49,7 +48,7 @@
@Parameters(name = "{0}")
public static TestParametersCollection data() {
- return getTestParameters().withAllRuntimes().build();
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
}
public FieldBitAccessInfoTest(TestParameters parameters) {
@@ -61,7 +60,7 @@
testForR8(parameters.getBackend())
.addProgramClasses(TestClass.class)
.addKeepMainRule(TestClass.class)
- .setMinApi(parameters.getRuntime())
+ .setMinApi(parameters.getApiLevel())
.compile()
.run(parameters.getRuntime(), TestClass.class)
.assertSuccessWithOutputLines(
@@ -93,7 +92,7 @@
clazz.forEachProgramMethod(
method -> {
IRCode code = method.buildIR(appView);
- fieldAccessAnalysis.recordFieldAccesses(code, feedback, new MethodProcessorMock());
+ fieldAccessAnalysis.recordFieldAccesses(code, feedback, new PrimaryMethodProcessorMock());
});
int bitsReadInBitField = feedback.bitsReadPerField.getInt(uniqueFieldByName(clazz, "bitField"));
@@ -116,7 +115,7 @@
}
}
- private AppView<AppInfoWithClassHierarchy> buildApp() throws IOException, ExecutionException {
+ private AppView<AppInfoWithClassHierarchy> buildApp() throws IOException {
DexItemFactory dexItemFactory = new DexItemFactory();
InternalOptions options = new InternalOptions(dexItemFactory, new Reporter());
options.programConsumer =
@@ -210,12 +209,7 @@
}
}
- static class MethodProcessorMock extends MethodProcessor {
-
- @Override
- public Phase getPhase() {
- return Phase.PRIMARY;
- }
+ static class PrimaryMethodProcessorMock extends MethodProcessor {
@Override
public boolean shouldApplyCodeRewritings(ProgramMethod method) {
@@ -223,6 +217,11 @@
}
@Override
+ public boolean isPrimaryMethodProcessor() {
+ return true;
+ }
+
+ @Override
public boolean isProcessedConcurrently(ProgramMethod method) {
return false;
}
diff --git a/src/test/java/com/android/tools/r8/ir/analysis/type/MissingClassesJoinTest.java b/src/test/java/com/android/tools/r8/ir/analysis/type/MissingClassesJoinTest.java
index 6d3c6cb..e654b89 100644
--- a/src/test/java/com/android/tools/r8/ir/analysis/type/MissingClassesJoinTest.java
+++ b/src/test/java/com/android/tools/r8/ir/analysis/type/MissingClassesJoinTest.java
@@ -69,6 +69,7 @@
.addProgramClasses(A.class, ASub1.class, Box.class, TestClass.class)
.addKeepAllClassesRule()
.addOptionsModification(options -> options.testing.allowTypeErrors = allowTypeErrors)
+ .addDontWarn(ASub2.class)
.allowDiagnosticWarningMessages()
.enableNoVerticalClassMergingAnnotations()
.setMinApi(parameters.getApiLevel())
diff --git a/src/test/java/com/android/tools/r8/ir/analysis/type/NullabilityTest.java b/src/test/java/com/android/tools/r8/ir/analysis/type/NullabilityTest.java
index a4c9785..3a6f0e4 100644
--- a/src/test/java/com/android/tools/r8/ir/analysis/type/NullabilityTest.java
+++ b/src/test/java/com/android/tools/r8/ir/analysis/type/NullabilityTest.java
@@ -13,7 +13,6 @@
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
-import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.ir.code.Argument;
@@ -33,6 +32,7 @@
import com.android.tools.r8.ir.optimize.nonnull.NonNullAfterFieldAccess;
import com.android.tools.r8.ir.optimize.nonnull.NonNullAfterInvoke;
import com.android.tools.r8.naming.MemberNaming.MethodSignature;
+import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.android.tools.r8.utils.DescriptorUtils;
import com.android.tools.r8.utils.Timing;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
@@ -63,7 +63,7 @@
boolean npeCaught,
BiConsumer<AppView<?>, IRCode> inspector)
throws Exception {
- AppView<? extends AppInfoWithClassHierarchy> appView = build(mainClass);
+ AppView<AppInfoWithLiveness> appView = build(mainClass);
CodeInspector codeInspector = new CodeInspector(appView.appInfo().app());
MethodSubject fooSubject = codeInspector.clazz(mainClass.getName()).method(signature);
IRCode irCode = fooSubject.buildIR();
diff --git a/src/test/java/com/android/tools/r8/ir/analysis/type/TypeElementWidthTest.java b/src/test/java/com/android/tools/r8/ir/analysis/type/TypeElementWidthTest.java
index 304d344..c07c431 100644
--- a/src/test/java/com/android/tools/r8/ir/analysis/type/TypeElementWidthTest.java
+++ b/src/test/java/com/android/tools/r8/ir/analysis/type/TypeElementWidthTest.java
@@ -10,7 +10,6 @@
import com.android.tools.r8.TestBase;
import com.android.tools.r8.graph.DexItemFactory;
-import com.google.common.collect.ImmutableSet;
import org.junit.Test;
public class TypeElementWidthTest extends TestBase {
@@ -78,7 +77,7 @@
DexItemFactory dexItemFactory = new DexItemFactory();
ClassTypeElement referenceType =
ClassTypeElement.create(
- dexItemFactory.objectType, Nullability.maybeNull(), ImmutableSet.of());
+ dexItemFactory.objectType, Nullability.maybeNull(), InterfaceCollection.empty());
assertFalse(referenceType.isSinglePrimitive());
assertFalse(referenceType.isWidePrimitive());
assertEquals(1, referenceType.requiredRegisters());
diff --git a/src/test/java/com/android/tools/r8/ir/analysis/type/TypeLatticeTest.java b/src/test/java/com/android/tools/r8/ir/analysis/type/TypeLatticeTest.java
index 5cdd0ca..4f15b2d 100644
--- a/src/test/java/com/android/tools/r8/ir/analysis/type/TypeLatticeTest.java
+++ b/src/test/java/com/android/tools/r8/ir/analysis/type/TypeLatticeTest.java
@@ -20,6 +20,7 @@
import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.DirectMappedDexApplication;
+import com.android.tools.r8.ir.analysis.type.InterfaceCollection.Builder;
import com.android.tools.r8.utils.AndroidApp;
import com.android.tools.r8.utils.DescriptorUtils;
import com.android.tools.r8.utils.InternalOptions;
@@ -554,6 +555,14 @@
Nullability.bottom(), ReferenceTypeElement.getNull().asMeetWithNotNull().nullability());
}
+ private static InterfaceCollection itfs(DexType... types) {
+ Builder builder = InterfaceCollection.builder();
+ for (DexType type : types) {
+ builder.addInterface(type, true);
+ }
+ return builder.build();
+ }
+
@Test
public void testLeastUpperBoundOfInterfaces() {
DexType collection = factory.createType("Ljava/util/Collection;");
@@ -561,37 +570,32 @@
DexType list = factory.createType("Ljava/util/List;");
DexType serializable = factory.serializableType;
- Set<DexType> lub =
- computeLeastUpperBoundOfInterfaces(appView, ImmutableSet.of(set), ImmutableSet.of(list));
+ InterfaceCollection lub = computeLeastUpperBoundOfInterfaces(appView, itfs(set), itfs(list));
assertEquals(1, lub.size());
- assertTrue(lub.contains(collection));
- verifyViaPairwiseJoin(lub,
- ImmutableSet.of(set), ImmutableSet.of(list));
+ assertTrue(lub.containsKnownInterface(collection));
+ assertEquals(collection, lub.getSingleKnownInterface());
+ verifyViaPairwiseJoin(lub, ImmutableSet.of(set), ImmutableSet.of(list));
lub =
computeLeastUpperBoundOfInterfaces(
- appView, ImmutableSet.of(set, serializable), ImmutableSet.of(list, serializable));
+ appView, itfs(set, serializable), itfs(list, serializable));
assertEquals(2, lub.size());
- assertTrue(lub.contains(collection));
- assertTrue(lub.contains(serializable));
+ assertTrue(lub.containsKnownInterface(collection));
+ assertTrue(lub.containsKnownInterface(serializable));
verifyViaPairwiseJoin(lub,
ImmutableSet.of(set, serializable), ImmutableSet.of(list, serializable));
- lub =
- computeLeastUpperBoundOfInterfaces(
- appView, ImmutableSet.of(set), ImmutableSet.of(list, serializable));
+ lub = computeLeastUpperBoundOfInterfaces(appView, itfs(set), itfs(list, serializable));
assertEquals(1, lub.size());
- assertTrue(lub.contains(collection));
- assertFalse(lub.contains(serializable));
+ assertTrue(lub.containsKnownInterface(collection));
+ assertFalse(lub.containsKnownInterface(serializable));
verifyViaPairwiseJoin(lub,
ImmutableSet.of(set), ImmutableSet.of(list, serializable));
- lub =
- computeLeastUpperBoundOfInterfaces(
- appView, ImmutableSet.of(set, serializable), ImmutableSet.of(list));
+ lub = computeLeastUpperBoundOfInterfaces(appView, itfs(set, serializable), itfs(list));
assertEquals(1, lub.size());
- assertTrue(lub.contains(collection));
- assertFalse(lub.contains(serializable));
+ assertTrue(lub.containsKnownInterface(collection));
+ assertFalse(lub.containsKnownInterface(serializable));
verifyViaPairwiseJoin(lub,
ImmutableSet.of(set, serializable), ImmutableSet.of(list));
@@ -599,16 +603,14 @@
DexType wType = factory.createType("Ljava/lang/reflect/WildcardType;");
DexType pType = factory.createType("Ljava/lang/reflect/ParameterizedType;");
- lub =
- computeLeastUpperBoundOfInterfaces(appView, ImmutableSet.of(wType), ImmutableSet.of(pType));
+ lub = computeLeastUpperBoundOfInterfaces(appView, itfs(wType), itfs(pType));
assertEquals(1, lub.size());
- assertTrue(lub.contains(type));
+ assertTrue(lub.containsKnownInterface(type));
+ assertEquals(type, lub.getSingleKnownInterface());
verifyViaPairwiseJoin(lub,
ImmutableSet.of(wType), ImmutableSet.of(pType));
- lub =
- computeLeastUpperBoundOfInterfaces(
- appView, ImmutableSet.of(list, serializable), ImmutableSet.of(pType));
+ lub = computeLeastUpperBoundOfInterfaces(appView, itfs(list, serializable), itfs(pType));
assertEquals(0, lub.size());
verifyViaPairwiseJoin(lub,
ImmutableSet.of(list, serializable), ImmutableSet.of(pType));
@@ -621,41 +623,36 @@
DescriptorUtils.javaTypeToDescriptor(I3.class.getCanonicalName()));
DexType i4 = factory.createType(
DescriptorUtils.javaTypeToDescriptor(I4.class.getCanonicalName()));
- lub = computeLeastUpperBoundOfInterfaces(appView, ImmutableSet.of(i3), ImmutableSet.of(i4));
+ lub = computeLeastUpperBoundOfInterfaces(appView, itfs(i3), itfs(i4));
assertEquals(2, lub.size());
- assertTrue(lub.contains(i1));
- assertTrue(lub.contains(i2));
+ assertTrue(lub.containsKnownInterface(i1));
+ assertTrue(lub.containsKnownInterface(i2));
verifyViaPairwiseJoin(lub,
ImmutableSet.of(i3), ImmutableSet.of(i4));
}
- private void verifyViaPairwiseJoin(Set<DexType> lub, Set<DexType> s1, Set<DexType>s2) {
- ImmutableSet.Builder<DexType> builder = ImmutableSet.builder();
+ private void verifyViaPairwiseJoin(InterfaceCollection lub, Set<DexType> s1, Set<DexType> s2) {
+ InterfaceCollection.Builder builder = InterfaceCollection.builder();
for (DexType i1 : s1) {
for (DexType i2 : s2) {
- Set<DexType> lubPerPair =
- computeLeastUpperBoundOfInterfaces(appView, ImmutableSet.of(i1), ImmutableSet.of(i2));
- for (DexType lubInterface : lubPerPair) {
- builder.add(lubInterface);
- }
+ InterfaceCollection lubPerPair =
+ computeLeastUpperBoundOfInterfaces(
+ appView, InterfaceCollection.singleton(i1), InterfaceCollection.singleton(i2));
+ lubPerPair.forEach(builder::addInterface);
}
}
- Set<DexType> pairwiseJoin = builder.build();
- ImmutableSet.Builder<DexType> lubBuilder = ImmutableSet.builder();
- for (DexType itf : pairwiseJoin) {
- // If there is a strict sub interface of this interface, it is not the least element.
- if (pairwiseJoin.stream()
- .anyMatch(other -> appView.appInfo().isStrictSubtypeOf(other, itf))) {
- continue;
- }
- lubBuilder.add(itf);
- }
- Set<DexType> pairwiseLub = lubBuilder.build();
-
- assertEquals(pairwiseLub.size(), lub.size());
- for (DexType i : pairwiseLub) {
- assertTrue(lub.contains(i));
- }
+ InterfaceCollection pairwiseJoin = builder.build();
+ InterfaceCollection.Builder lubBuilder = InterfaceCollection.builder();
+ pairwiseJoin.forEach(
+ (itf, isKnown) -> {
+ // If there is a strict sub interface of this interface, it is not the least element.
+ if (!pairwiseJoin.anyMatch(
+ (other, ignoredOtherIsKnown) -> appView.appInfo().isStrictSubtypeOf(other, itf))) {
+ lubBuilder.addInterface(itf, isKnown);
+ }
+ });
+ InterfaceCollection pairwiseLub = lubBuilder.build();
+ assertEquals(pairwiseLub, lub);
}
}
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/NonNullTrackerTest.java b/src/test/java/com/android/tools/r8/ir/optimize/NonNullTrackerTest.java
index daa21f4..f5431bb 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/NonNullTrackerTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/NonNullTrackerTest.java
@@ -9,7 +9,6 @@
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
-import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.ir.code.IRCode;
import com.android.tools.r8.ir.code.InstancePut;
@@ -22,6 +21,7 @@
import com.android.tools.r8.ir.optimize.nonnull.NonNullAfterInvoke;
import com.android.tools.r8.ir.optimize.nonnull.NonNullAfterNullCheck;
import com.android.tools.r8.naming.MemberNaming.MethodSignature;
+import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.android.tools.r8.utils.Timing;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
import com.android.tools.r8.utils.codeinspector.MethodSubject;
@@ -49,7 +49,7 @@
int expectedNumberOfNonNull,
Consumer<IRCode> testAugmentedIRCode)
throws Exception {
- AppView<? extends AppInfoWithClassHierarchy> appView = build(testClass);
+ AppView<AppInfoWithLiveness> appView = build(testClass);
CodeInspector codeInspector = new CodeInspector(appView.appInfo().app());
MethodSubject fooSubject = codeInspector.clazz(testClass.getName()).method(signature);
IRCode code = fooSubject.buildIR();
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/NonNullTrackerTestBase.java b/src/test/java/com/android/tools/r8/ir/optimize/NonNullTrackerTestBase.java
index 6b5f0cb..7d5e23e 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/NonNullTrackerTestBase.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/NonNullTrackerTestBase.java
@@ -5,13 +5,12 @@
import com.android.tools.r8.TestBase;
import com.android.tools.r8.ToolHelper;
-import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.shaking.AppInfoWithLiveness;
public abstract class NonNullTrackerTestBase extends TestBase {
- protected AppView<? extends AppInfoWithClassHierarchy> build(Class<?> mainClass)
- throws Exception {
- return computeAppViewWithSubtyping(buildAndroidApp(ToolHelper.getClassAsBytes(mainClass)));
+ protected AppView<AppInfoWithLiveness> build(Class<?> mainClass) throws Exception {
+ return computeAppViewWithLiveness(buildAndroidApp(ToolHelper.getClassAsBytes(mainClass)));
}
}
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/SimplifyIfNotNullTest.java b/src/test/java/com/android/tools/r8/ir/optimize/SimplifyIfNotNullTest.java
index 14d7e6b..da19669 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/SimplifyIfNotNullTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/SimplifyIfNotNullTest.java
@@ -5,7 +5,9 @@
import static org.junit.Assert.assertEquals;
+import com.android.tools.r8.R8FullTestBuilder;
import com.android.tools.r8.TestBase;
+import com.android.tools.r8.ThrowableConsumer;
import com.android.tools.r8.ir.optimize.nonnull.FieldAccessTest;
import com.android.tools.r8.ir.optimize.nonnull.NonNullAfterArrayAccess;
import com.android.tools.r8.ir.optimize.nonnull.NonNullAfterFieldAccess;
@@ -32,10 +34,19 @@
}
private void testR8(Class<?> testClass, List<MethodSignature> signatures) throws Exception {
+ testR8(testClass, signatures, null);
+ }
+
+ private void testR8(
+ Class<?> testClass,
+ List<MethodSignature> signatures,
+ ThrowableConsumer<R8FullTestBuilder> configuration)
+ throws Exception {
CodeInspector codeInspector =
testForR8(Backend.DEX)
.addProgramClasses(testClass)
.addKeepRules("-keep class " + testClass.getCanonicalName() + " { *; }")
+ .apply(configuration)
.compile()
.inspector();
verifyAbsenceOfIf(codeInspector, testClass, signatures);
@@ -67,6 +78,9 @@
new String[]{FieldAccessTest.class.getCanonicalName()});
MethodSignature foo2 = new MethodSignature("foo2", "int",
new String[]{FieldAccessTest.class.getCanonicalName()});
- testR8(NonNullAfterFieldAccess.class, ImmutableList.of(foo, bar, foo2));
+ testR8(
+ NonNullAfterFieldAccess.class,
+ ImmutableList.of(foo, bar, foo2),
+ testBuilder -> testBuilder.addProgramClasses(FieldAccessTest.class));
}
}
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/SubsumedCatchHandlerTest.java b/src/test/java/com/android/tools/r8/ir/optimize/SubsumedCatchHandlerTest.java
index 87de375..19d7226 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/SubsumedCatchHandlerTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/SubsumedCatchHandlerTest.java
@@ -86,6 +86,7 @@
testForR8(backend)
.addInnerClasses(SubsumedCatchHandlerTest.class)
.addKeepMainRule(TestClass.class)
+ .enableForceInliningAnnotations()
.enableInliningAnnotations()
.run(TestClass.class)
.assertSuccessWithOutput(expected)
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/canonicalization/EffectivelyFinalFieldCanonicalizationTest.java b/src/test/java/com/android/tools/r8/ir/optimize/canonicalization/EffectivelyFinalFieldCanonicalizationTest.java
index 4e73445..59eff5b 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/canonicalization/EffectivelyFinalFieldCanonicalizationTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/canonicalization/EffectivelyFinalFieldCanonicalizationTest.java
@@ -40,6 +40,8 @@
testForR8(parameters.getBackend())
.addInnerClasses(EffectivelyFinalFieldCanonicalizationTest.class)
.addKeepMainRule(TestClass.class)
+ .enableInliningAnnotations()
+ .enableNeverClassInliningAnnotations()
.setMinApi(parameters.getApiLevel())
.compile()
.inspect(this::inspect)
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/checkcast/CastToUninstantiatedClassTest.java b/src/test/java/com/android/tools/r8/ir/optimize/checkcast/CastToUninstantiatedClassTest.java
index aa385fd..895e6ab 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/checkcast/CastToUninstantiatedClassTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/checkcast/CastToUninstantiatedClassTest.java
@@ -25,8 +25,7 @@
@Parameters(name = "{0}")
public static TestParametersCollection data() {
- // TODO(b/172194277): Add support for synthetics when generating CF.
- return getTestParameters().withDexRuntimes().withAllApiLevels().build();
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
}
public CastToUninstantiatedClassTest(TestParameters parameters) {
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/classinitializerdefaults/NonFinalFieldWithDefaultValueTest.java b/src/test/java/com/android/tools/r8/ir/optimize/classinitializerdefaults/NonFinalFieldWithDefaultValueTest.java
index c9033f5..2922b90 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/classinitializerdefaults/NonFinalFieldWithDefaultValueTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/classinitializerdefaults/NonFinalFieldWithDefaultValueTest.java
@@ -58,6 +58,7 @@
testForR8(parameters.getBackend())
.addInnerClasses(NonFinalFieldWithDefaultValueTest.class)
.addKeepMainRule(TestClass.class)
+ .enableInliningAnnotations()
.setMinApi(parameters.getApiLevel())
.compile()
.inspect(this::inspect)
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/classinliner/ClassInlinerTest.java b/src/test/java/com/android/tools/r8/ir/optimize/classinliner/ClassInlinerTest.java
index e0266bd..38bb02e 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/classinliner/ClassInlinerTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/classinliner/ClassInlinerTest.java
@@ -4,7 +4,6 @@
package com.android.tools.r8.ir.optimize.classinliner;
-import static com.android.tools.r8.ir.desugar.LambdaRewriter.LAMBDA_CLASS_NAME_PREFIX;
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;
@@ -322,8 +321,8 @@
Set<String> expectedTypes = Sets.newHashSet("java.lang.StringBuilder");
expectedTypes.addAll(
inspector.allClasses().stream()
+ .filter(FoundClassSubject::isSynthesizedJavaLambdaClass)
.map(FoundClassSubject::getFinalName)
- .filter(name -> name.contains(LAMBDA_CLASS_NAME_PREFIX))
.collect(Collectors.toList()));
assertEquals(expectedTypes, collectTypes(clazz.uniqueMethodWithName("testStatefulLambda")));
assertTrue(
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/classinliner/ExtraMethodNullTest.java b/src/test/java/com/android/tools/r8/ir/optimize/classinliner/ExtraMethodNullTest.java
index 3ece2da..9890166 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/classinliner/ExtraMethodNullTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/classinliner/ExtraMethodNullTest.java
@@ -4,21 +4,36 @@
package com.android.tools.r8.ir.optimize.classinliner;
-import static org.hamcrest.CoreMatchers.containsString;
-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 org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+@RunWith(Parameterized.class)
public class ExtraMethodNullTest extends TestBase {
+ private final TestParameters parameters;
+
+ @Parameterized.Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
+ }
+
+ public ExtraMethodNullTest(TestParameters parameters) {
+ this.parameters = parameters;
+ }
+
@Test
public void test() throws Exception {
- testForR8(Backend.DEX)
+ testForR8(parameters.getBackend())
.addProgramClassesAndInnerClasses(One.class)
.addKeepMainRule(One.class)
- .run(One.class)
- .assertFailureWithErrorThatMatches(containsString("java.lang.NullPointerException"));
+ .setMinApi(parameters.getApiLevel())
+ .run(parameters.getRuntime(), One.class)
+ .assertFailureWithErrorThatThrows(NullPointerException.class);
}
public static class One {
@@ -30,7 +45,6 @@
}
static class Other {
- @NeverInline
Object print(Object one) {
return one;
}
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/devirtualize/InterfaceRenewalInLoopDebugTestRunner.java b/src/test/java/com/android/tools/r8/ir/optimize/devirtualize/InterfaceRenewalInLoopDebugTestRunner.java
index b21e168..be426b1 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/devirtualize/InterfaceRenewalInLoopDebugTestRunner.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/devirtualize/InterfaceRenewalInLoopDebugTestRunner.java
@@ -26,13 +26,15 @@
@Test
public void test() throws Throwable {
- R8TestCompileResult result = testForR8(Backend.CF)
- .setMode(CompilationMode.DEBUG)
- .addProgramClasses(TestInterface.class, IMPL, MAIN)
- .addKeepMainRule(MAIN)
- .addKeepRules(ImmutableList.of("-keepattributes SourceFile,LineNumberTable"))
- .noMinification()
- .compile();
+ R8TestCompileResult result =
+ testForR8(Backend.CF)
+ .setMode(CompilationMode.DEBUG)
+ .addProgramClasses(TestInterface.class, IMPL, MAIN)
+ .addKeepMainRule(MAIN)
+ .addKeepRules(ImmutableList.of("-keepattributes SourceFile,LineNumberTable"))
+ .enableNoVerticalClassMergingAnnotations()
+ .noMinification()
+ .compile();
CodeInspector inspector = result.inspector();
ClassSubject mainSubject = inspector.clazz(MAIN);
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/fields/NonFinalFinalFieldTest.java b/src/test/java/com/android/tools/r8/ir/optimize/fields/NonFinalFinalFieldTest.java
index 292f74e..d8cdbc5 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/fields/NonFinalFinalFieldTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/fields/NonFinalFinalFieldTest.java
@@ -49,6 +49,7 @@
.addProgramClasses(TestClass.class)
.addProgramClassFileData(getProgramClassFileData())
.addKeepMainRule(TestClass.class)
+ .enableNeverClassInliningAnnotations()
.setMinApi(parameters.getApiLevel())
.compile()
.run(parameters.getRuntime(), TestClass.class)
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/inliner/InlineMappingOnSameLineTest.java b/src/test/java/com/android/tools/r8/ir/optimize/inliner/InlineMappingOnSameLineTest.java
index a8abf65..c4a0b9d 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/inliner/InlineMappingOnSameLineTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/inliner/InlineMappingOnSameLineTest.java
@@ -9,6 +9,7 @@
import com.android.tools.r8.CompilationMode;
import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.R8TestBuilder;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.naming.retrace.RetraceTestBase;
import com.android.tools.r8.naming.retrace.StackTrace;
@@ -46,6 +47,11 @@
}
@Override
+ public void configure(R8TestBuilder<?> builder) {
+ builder.enableInliningAnnotations();
+ }
+
+ @Override
public Class<?> getMainClass() {
return Main.class;
}
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/inliner/InliningIntoVisibilityBridgeTest.java b/src/test/java/com/android/tools/r8/ir/optimize/inliner/InliningIntoVisibilityBridgeTest.java
index a3b0c4a..46727f2 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/inliner/InliningIntoVisibilityBridgeTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/inliner/InliningIntoVisibilityBridgeTest.java
@@ -10,6 +10,7 @@
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.Assert.assertEquals;
+import com.android.tools.r8.R8TestBuilder;
import com.android.tools.r8.R8TestRunResult;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.ir.optimize.inliner.testclasses.InliningIntoVisibilityBridgeTestClasses;
@@ -47,10 +48,10 @@
.addInnerClasses(InliningIntoVisibilityBridgeTest.class)
.addInnerClasses(InliningIntoVisibilityBridgeTestClasses.class)
.addKeepMainRule(TestClass.class)
- .addKeepRules(
- neverInline
- ? ("-neverinline class " + getClassA().getTypeName() + " { method(); }")
- : "")
+ .addForceInliningAnnotations()
+ .addInliningAnnotations()
+ .applyIf(neverInline, R8TestBuilder::enableInliningAnnotations)
+ .applyIf(!neverInline, R8TestBuilder::enableForceInliningAnnotations)
.enableNoVerticalClassMergingAnnotations()
.enableProguardTestOptions()
.compile()
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/inliner/MultipleIndirectCallSitesTest.java b/src/test/java/com/android/tools/r8/ir/optimize/inliner/MultipleIndirectCallSitesTest.java
index a0ed3e6..09bb71b 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/inliner/MultipleIndirectCallSitesTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/inliner/MultipleIndirectCallSitesTest.java
@@ -29,7 +29,8 @@
@Parameterized.Parameters(name = "{1}, invoke A.m(): {0}")
public static List<Object[]> data() {
- return buildParameters(BooleanUtils.values(), getTestParameters().withAllRuntimes().build());
+ return buildParameters(
+ BooleanUtils.values(), getTestParameters().withAllRuntimesAndApiLevels().build());
}
public MultipleIndirectCallSitesTest(boolean invokeMethodOnA, TestParameters parameters) {
@@ -47,6 +48,7 @@
// kept, there is a single call site that invokes A.m(). However, this does not mean that
// A.m() has a single call site, so it should not allow inlining of A.m().
.addKeepRules(getKeepRules())
+ .enableNeverClassInliningAnnotations()
.setMinApi(parameters.getRuntime())
.compile()
.inspect(this::verifyInlining)
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/inliner/Regress131349148.java b/src/test/java/com/android/tools/r8/ir/optimize/inliner/Regress131349148.java
index 09b0ae3..235aa07 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/inliner/Regress131349148.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/inliner/Regress131349148.java
@@ -26,7 +26,10 @@
@Parameters(name = "{0}")
public static TestParametersCollection params() {
- return getTestParameters().withDexRuntimes().withAllApiLevels().build();
+ return getTestParameters()
+ .withDexRuntimes()
+ .withApiLevelsStartingAtIncluding(AndroidApiLevel.K)
+ .build();
}
public Regress131349148(TestParameters parameters) {
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/inliner/testclasses/InliningIntoVisibilityBridgeTestClasses.java b/src/test/java/com/android/tools/r8/ir/optimize/inliner/testclasses/InliningIntoVisibilityBridgeTestClasses.java
index c8c60ae..54018ee 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/inliner/testclasses/InliningIntoVisibilityBridgeTestClasses.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/inliner/testclasses/InliningIntoVisibilityBridgeTestClasses.java
@@ -5,6 +5,7 @@
package com.android.tools.r8.ir.optimize.inliner.testclasses;
import com.android.tools.r8.ForceInline;
+import com.android.tools.r8.NeverInline;
import com.android.tools.r8.NoVerticalClassMerging;
public class InliningIntoVisibilityBridgeTestClasses {
@@ -17,6 +18,7 @@
static class InliningIntoVisibilityBridgeTestClassA {
@ForceInline
+ @NeverInline
public static void method() {
System.out.println("Hello world");
}
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/instanceofremoval/ZipFileInstanceOfAutoCloseableTest.java b/src/test/java/com/android/tools/r8/ir/optimize/instanceofremoval/ZipFileInstanceOfAutoCloseableTest.java
new file mode 100644
index 0000000..b15cca9
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/ir/optimize/instanceofremoval/ZipFileInstanceOfAutoCloseableTest.java
@@ -0,0 +1,226 @@
+// 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.instanceofremoval;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assume.assumeTrue;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.dex.ApplicationReader;
+import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.DirectMappedDexApplication;
+import com.android.tools.r8.ir.analysis.type.ClassTypeElement;
+import com.android.tools.r8.ir.analysis.type.InterfaceCollection;
+import com.android.tools.r8.ir.analysis.type.Nullability;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.AndroidApp;
+import com.android.tools.r8.utils.InternalOptions;
+import com.android.tools.r8.utils.StringUtils;
+import com.android.tools.r8.utils.Timing;
+import com.android.tools.r8.utils.ZipUtils.ZipBuilder;
+import java.io.IOException;
+import java.nio.channels.Channel;
+import java.nio.file.Path;
+import java.util.jar.JarFile;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class ZipFileInstanceOfAutoCloseableTest extends TestBase {
+
+ static final String EXPECTED_PRE_API_19 = StringUtils.lines("Not an AutoCloseable");
+ static final String EXPECTED_POST_API_19 = StringUtils.lines("Is an AutoCloseable");
+
+ private final TestParameters parameters;
+
+ @Parameterized.Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
+ }
+
+ public ZipFileInstanceOfAutoCloseableTest(TestParameters parameters) {
+ this.parameters = parameters;
+ }
+
+ private boolean runtimeZipFileIsCloseable() {
+ return parameters.isCfRuntime()
+ || parameters
+ .getRuntime()
+ .asDex()
+ .maxSupportedApiLevel()
+ .isGreaterThanOrEqualTo(AndroidApiLevel.K);
+ }
+
+ private String expectedOutput() {
+ return runtimeZipFileIsCloseable() ? EXPECTED_POST_API_19 : EXPECTED_PRE_API_19;
+ }
+
+ private Path getAndroidJar() {
+ // Always use an android jar later than API 19. Thus at compile-time ZipFile < Closeable.
+ return ToolHelper.getAndroidJar(AndroidApiLevel.LATEST);
+ }
+
+ private String getZipFile() throws IOException {
+ return ZipBuilder.builder(temp.newFile("file.zip").toPath())
+ .addBytes("entry", new byte[1])
+ .build()
+ .toString();
+ }
+
+ @Test
+ public void testD8() throws Exception {
+ assumeTrue(parameters.isDexRuntime());
+ testForD8()
+ .addInnerClasses(ZipFileInstanceOfAutoCloseableTest.class)
+ .setMinApi(parameters.getApiLevel())
+ .addLibraryFiles(getAndroidJar())
+ .run(parameters.getRuntime(), TestClass.class, getZipFile())
+ .assertSuccessWithOutput(expectedOutput());
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ testForR8(parameters.getBackend())
+ .addInnerClasses(ZipFileInstanceOfAutoCloseableTest.class)
+ .addKeepMainRule(TestClass.class)
+ .setMinApi(parameters.getApiLevel())
+ .addLibraryFiles(getAndroidJar())
+ .run(parameters.getRuntime(), TestClass.class, getZipFile())
+ .assertSuccessWithOutput(expectedOutput());
+ }
+
+ @Test
+ public void testTypeStructure() throws Exception {
+ assumeTrue(parameters.isDexRuntime());
+ // Set the min API and create the raw app.
+ InternalOptions options = new InternalOptions();
+ options.minApiLevel = parameters.getApiLevel().getLevel();
+ DirectMappedDexApplication application =
+ new ApplicationReader(
+ AndroidApp.builder()
+ .addProgramFiles(ToolHelper.getClassFileForTestClass(MyJarFileScanner.class))
+ .addLibraryFiles(getAndroidJar())
+ .build(),
+ options,
+ Timing.empty())
+ .read()
+ .toDirect();
+ AppView<AppInfoWithClassHierarchy> appView = AppView.createForR8(application);
+
+ // Type references.
+ DexType zipFileType = appView.dexItemFactory().createType("Ljava/util/zip/ZipFile;");
+ DexType closeableType = appView.dexItemFactory().createType("Ljava/io/Closeable;");
+ DexType autoCloseableType = appView.dexItemFactory().createType("Ljava/lang/AutoCloseable;");
+ DexType scannerType = appView.dexItemFactory().createType("Ljava/util/Scanner;");
+ DexType myJarFileChannel = buildType(MyJarFileScanner.class, appView.dexItemFactory());
+
+ // Read the computed interface types. Both the "type element" which is used in IR and the full
+ // computed dependencies.
+ ClassTypeElement zipFileTypeElement = makeTypeElement(appView, zipFileType);
+ {
+ InterfaceCollection directInterfaces = zipFileTypeElement.getInterfaces();
+ InterfaceCollection allInterfaces = appView.appInfo().implementedInterfaces(zipFileType);
+ if (zipFileHasCloseable()) {
+ // After API 19 / K the types are known and present.
+ assertEquals(closeableType, directInterfaces.getSingleKnownInterface());
+ assertTrue(allInterfaces.containsKnownInterface(closeableType));
+ assertTrue(allInterfaces.containsKnownInterface(autoCloseableType));
+ assertEquals(2, allInterfaces.size());
+ } else {
+ // The interfaces are still present due to the android jar for K, but they are marked
+ // unknown.
+ assertTrue(directInterfaces.contains(closeableType).isUnknown());
+ assertFalse(directInterfaces.containsKnownInterface(closeableType));
+ assertEquals(1, directInterfaces.size());
+ // Same for the collection of all interfaces. Since the
+ assertTrue(allInterfaces.contains(closeableType).isUnknown());
+ assertTrue(allInterfaces.contains(autoCloseableType).isUnknown());
+ assertFalse(allInterfaces.containsKnownInterface(closeableType));
+ assertFalse(allInterfaces.containsKnownInterface(autoCloseableType));
+ assertEquals(2, allInterfaces.size());
+ }
+ }
+
+ ClassTypeElement scannerTypeElement = makeTypeElement(appView, scannerType);
+ {
+ // Scanner implements Closable and Iterator on all APIs.
+ InterfaceCollection directInterfaces = scannerTypeElement.getInterfaces();
+ assertTrue(directInterfaces.containsKnownInterface(closeableType));
+ assertEquals(2, directInterfaces.size());
+
+ // Joining a type of known with a type of unknown should still be unknown.
+ ClassTypeElement joinLeft1 =
+ scannerTypeElement.join(zipFileTypeElement, appView).asClassType();
+ ClassTypeElement joinRight1 =
+ zipFileTypeElement.join(scannerTypeElement, appView).asClassType();
+ if (zipFileHasCloseable()) {
+ assertTrue(joinLeft1.getInterfaces().contains(closeableType).isTrue());
+ assertTrue(joinRight1.getInterfaces().contains(closeableType).isTrue());
+ } else {
+ assertTrue(joinLeft1.getInterfaces().contains(closeableType).isUnknown());
+ assertTrue(joinRight1.getInterfaces().contains(closeableType).isUnknown());
+ }
+ }
+
+ // Custom class derived from JarFile and Channel, thus it must implement closable.
+ ClassTypeElement myJarFileScannerTypeElement = makeTypeElement(appView, myJarFileChannel);
+
+ // Joining with Scanner will retain it as always Closeable.
+ ClassTypeElement joinWithScanner =
+ myJarFileScannerTypeElement.join(scannerTypeElement, appView).asClassType();
+ assertTrue(joinWithScanner.getInterfaces().contains(closeableType).isTrue());
+
+ // Joining with ZipFile will loose the assurance that it is Closeable.
+ ClassTypeElement joinWithZipFile =
+ myJarFileScannerTypeElement.join(zipFileTypeElement, appView).asClassType();
+ assertTrue(joinWithZipFile.getInterfaces().contains(closeableType).isPossiblyTrue());
+ assertEquals(
+ zipFileHasCloseable(), joinWithZipFile.getInterfaces().contains(closeableType).isTrue());
+ }
+
+ private boolean zipFileHasCloseable() {
+ return parameters.getApiLevel().isGreaterThanOrEqualTo(AndroidApiLevel.K);
+ }
+
+ private static ClassTypeElement makeTypeElement(
+ AppView<AppInfoWithClassHierarchy> appView, DexType type) {
+ return ClassTypeElement.create(type, Nullability.maybeNull(), appView);
+ }
+
+ static class MyJarFileScanner extends JarFile implements Channel {
+
+ public MyJarFileScanner(String name) throws IOException {
+ super(name);
+ }
+
+ @Override
+ public boolean isOpen() {
+ return false;
+ }
+ }
+
+ static class TestClass {
+
+ public static void foo(Object o) throws Exception {
+ if (o instanceof AutoCloseable) {
+ System.out.println("Is an AutoCloseable");
+ ((AutoCloseable) o).close();
+ } else {
+ System.out.println("Not an AutoCloseable");
+ }
+ }
+
+ public static void main(String[] args) throws Exception {
+ foo(new JarFile(args[0]));
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/membervaluepropagation/AssumeNotNullTest.java b/src/test/java/com/android/tools/r8/ir/optimize/membervaluepropagation/AssumeNotNullTest.java
new file mode 100644
index 0000000..fda891e
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/ir/optimize/membervaluepropagation/AssumeNotNullTest.java
@@ -0,0 +1,130 @@
+// 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.membervaluepropagation;
+
+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.NeverInline;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.android.tools.r8.utils.codeinspector.InstructionSubject;
+import com.android.tools.r8.utils.codeinspector.MethodSubject;
+import com.google.common.collect.ImmutableList;
+import java.util.List;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class AssumeNotNullTest extends TestBase {
+
+ private final String flavor;
+ private final TestParameters parameters;
+
+ @Parameters(name = "{1}, flavor: {0}")
+ public static List<Object[]> data() {
+ return buildParameters(
+ ImmutableList.of("assumenosideeffects", "assumevalues"),
+ getTestParameters().withAllRuntimesAndApiLevels().build());
+ }
+
+ public AssumeNotNullTest(String flavor, TestParameters parameters) {
+ this.flavor = flavor;
+ this.parameters = parameters;
+ }
+
+ @Test
+ public void test() throws Exception {
+ testForR8(parameters.getBackend())
+ .addInnerClasses(getClass())
+ .addKeepMainRule(Main.class)
+ .addKeepRules(
+ "-" + flavor + " class " + Factory.class.getTypeName() + " {",
+ " java.lang.Object create() return 1;",
+ "}",
+ "-" + flavor + " class " + Singleton.class.getTypeName() + " {",
+ " java.lang.Object INSTANCE return 1;",
+ "}")
+ .enableInliningAnnotations()
+ .setMinApi(parameters.getApiLevel())
+ .compile()
+ .inspect(
+ inspector -> {
+ MethodSubject mainMethodSubject = inspector.clazz(Main.class).mainMethod();
+ assertThat(mainMethodSubject, isPresent());
+ if (flavor.equals("assumenosideeffects")) {
+ // With -assumenosideeffects, the method should become empty.
+ assertTrue(
+ mainMethodSubject
+ .streamInstructions()
+ .allMatch(InstructionSubject::isReturnVoid));
+ } else {
+ // With -assumevalues, the Singleton.INSTANCE access should remain along with the
+ // Factory.create() invoke.
+ ClassSubject factoryClassSubject = inspector.clazz(Factory.class);
+ assertThat(factoryClassSubject, isPresent());
+
+ ClassSubject singletonClassSubject = inspector.clazz(Singleton.class);
+ assertThat(singletonClassSubject, isPresent());
+
+ assertEquals(
+ 2,
+ mainMethodSubject
+ .streamInstructions()
+ .filter(
+ instruction -> instruction.isFieldAccess() || instruction.isInvoke())
+ .count());
+ assertTrue(
+ mainMethodSubject
+ .streamInstructions()
+ .anyMatch(
+ instruction ->
+ instruction.isStaticGet()
+ && instruction.getField().getHolderType()
+ == singletonClassSubject.getDexProgramClass().getType()));
+ assertTrue(
+ mainMethodSubject
+ .streamInstructions()
+ .filter(InstructionSubject::isInvoke)
+ .anyMatch(
+ instruction ->
+ instruction.getMethod().getHolderType()
+ == factoryClassSubject.getDexProgramClass().getType()));
+ }
+ })
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithEmptyOutput();
+ }
+
+ static class Main {
+
+ public static void main(String[] args) {
+ if (Singleton.INSTANCE == null) {
+ System.out.println("Foo");
+ }
+ if (Factory.create() == null) {
+ System.out.println("Bar");
+ }
+ }
+ }
+
+ static class Factory {
+
+ @NeverInline
+ public static Object create() {
+ return System.currentTimeMillis() > 0 ? new Object() : null;
+ }
+ }
+
+ static class Singleton {
+
+ public static Object INSTANCE = System.currentTimeMillis() > 0 ? new Object() : null;
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/membervaluepropagation/B143686595.java b/src/test/java/com/android/tools/r8/ir/optimize/membervaluepropagation/B143686595.java
index 036bde7..4f95f4d 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/membervaluepropagation/B143686595.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/membervaluepropagation/B143686595.java
@@ -39,6 +39,7 @@
.addProgramClasses(TestClass.class)
.addClasspathClasses(I.class)
.addKeepMainRule(TestClass.class)
+ .enableInliningAnnotations()
.setMinApi(parameters.getApiLevel())
.compile()
.addRunClasspathFiles(libraryResult.writeToZip())
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/membervaluepropagation/InstanceFieldValuePropagationWithMultipleInstanceInitializersTest.java b/src/test/java/com/android/tools/r8/ir/optimize/membervaluepropagation/InstanceFieldValuePropagationWithMultipleInstanceInitializersTest.java
index d2b739e..6c7cf37 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/membervaluepropagation/InstanceFieldValuePropagationWithMultipleInstanceInitializersTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/membervaluepropagation/InstanceFieldValuePropagationWithMultipleInstanceInitializersTest.java
@@ -40,6 +40,7 @@
testForR8(parameters.getBackend())
.addInnerClasses(InstanceFieldValuePropagationWithMultipleInstanceInitializersTest.class)
.addKeepMainRule(TestClass.class)
+ .enableNeverClassInliningAnnotations()
.setMinApi(parameters.getApiLevel())
.compile()
.inspect(this::inspect)
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/outliner/OutlineFromStaticInterfaceMethodTest.java b/src/test/java/com/android/tools/r8/ir/optimize/outliner/OutlineFromStaticInterfaceMethodTest.java
index ec4c859..c0267f2 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/outliner/OutlineFromStaticInterfaceMethodTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/outliner/OutlineFromStaticInterfaceMethodTest.java
@@ -50,6 +50,7 @@
options.outline.minSize = 2;
})
.enableInliningAnnotations()
+ .enableNeverClassInliningAnnotations()
.setMinApi(parameters.getApiLevel())
.compile()
.inspect(this::inspect)
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/reflection/GetClassTest.java b/src/test/java/com/android/tools/r8/ir/optimize/reflection/GetClassTest.java
index 3e28157..d1d5f1f 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/reflection/GetClassTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/reflection/GetClassTest.java
@@ -13,6 +13,7 @@
import com.android.tools.r8.ForceInline;
import com.android.tools.r8.NeverInline;
import com.android.tools.r8.NoHorizontalClassMerging;
+import com.android.tools.r8.R8TestBuilder;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.ListUtils;
@@ -218,6 +219,8 @@
testForR8(parameters.getBackend())
.setMode(mode)
.addInnerClasses(GetClassTest.class)
+ .addForceInliningAnnotations()
+ .applyIf(mode == CompilationMode.RELEASE, R8TestBuilder::enableForceInliningAnnotations)
.enableInliningAnnotations()
.enableNoHorizontalClassMergingAnnotations()
.addKeepMainRule(MAIN)
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/reflection/GetSimpleNameTest.java b/src/test/java/com/android/tools/r8/ir/optimize/reflection/GetSimpleNameTest.java
index a9a5c6f..81612ee 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/reflection/GetSimpleNameTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/reflection/GetSimpleNameTest.java
@@ -214,6 +214,7 @@
R8TestRunResult result =
testForR8(parameters.getBackend())
.addProgramFiles(classPaths)
+ .enableForceInliningAnnotations()
.enableInliningAnnotations()
.addKeepMainRule(MAIN)
.addKeepRules("-keep class **.ClassGetSimpleName*")
@@ -243,6 +244,7 @@
.addKeepRules("-keep,allowobfuscation class **.Outer*")
.addKeepAttributes("InnerClasses", "EnclosingMethod")
.addKeepRules("-printmapping " + createNewMappingPath().toAbsolutePath().toString())
+ .enableForceInliningAnnotations()
.minification(enableMinification)
.setMinApi(parameters.getApiLevel())
.addOptionsModification(this::configure)
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/staticizer/ClassStaticizerTest.java b/src/test/java/com/android/tools/r8/ir/optimize/staticizer/ClassStaticizerTest.java
index 9ee6ad7..6e91c38 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/staticizer/ClassStaticizerTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/staticizer/ClassStaticizerTest.java
@@ -116,6 +116,7 @@
.addKeepMainRule(main)
.addKeepAttributes("InnerClasses", "EnclosingMethod")
.addOptionsModification(this::configure)
+ .enableInliningAnnotations()
.setMinApi(parameters.getApiLevel())
.run(parameters.getRuntime(), main)
.assertSuccessWithOutput(EXPECTED);
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/string/StringLengthTest.java b/src/test/java/com/android/tools/r8/ir/optimize/string/StringLengthTest.java
index 59c3d5c..7924a77 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/string/StringLengthTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/string/StringLengthTest.java
@@ -96,6 +96,7 @@
R8TestRunResult result =
testForR8(parameters.getBackend())
.addProgramClasses(MAIN)
+ .enableForceInliningAnnotations()
.enableInliningAnnotations()
.addKeepMainRule(MAIN)
.setMinApi(parameters.getApiLevel())
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/string/StringValueOfTest.java b/src/test/java/com/android/tools/r8/ir/optimize/string/StringValueOfTest.java
index 7d68bda..2e1709c 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/string/StringValueOfTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/string/StringValueOfTest.java
@@ -111,6 +111,7 @@
SingleTestRunResult<?> result =
testForR8(parameters.getBackend())
.addProgramClassesAndInnerClasses(MAIN)
+ .enableForceInliningAnnotations()
.enableInliningAnnotations()
.enableMemberValuePropagationAnnotations()
.addKeepMainRule(MAIN)
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/switches/StringSwitchWitNonConstIdTest.java b/src/test/java/com/android/tools/r8/ir/optimize/switches/StringSwitchWitNonConstIdTest.java
new file mode 100644
index 0000000..c993353
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/ir/optimize/switches/StringSwitchWitNonConstIdTest.java
@@ -0,0 +1,106 @@
+// 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.switches;
+
+import static org.junit.Assume.assumeTrue;
+
+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 org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class StringSwitchWitNonConstIdTest extends TestBase {
+
+ private final TestParameters parameters;
+
+ @Parameterized.Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
+ }
+
+ public StringSwitchWitNonConstIdTest(TestParameters parameters) {
+ this.parameters = parameters;
+ }
+
+ @Test
+ public void testD8() throws Exception {
+ assumeTrue(parameters.isDexRuntime());
+ testForD8()
+ .addProgramClasses(Main.class)
+ .release()
+ .setMinApi(parameters.getApiLevel())
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutputLines("Foo", "Bar", "Baz", "Qux");
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ testForR8(parameters.getBackend())
+ .addProgramClasses(Main.class)
+ .addKeepMainRule(Main.class)
+ .enableInliningAnnotations()
+ .release()
+ .setMinApi(parameters.getApiLevel())
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutputLines("Foo", "Bar", "Baz", "Qux");
+ }
+
+ static class Main {
+
+ public static void main(String[] args) {
+ test("Foo");
+ test("Bar");
+ test("Baz");
+ test("Qux");
+ }
+
+ @NeverInline
+ static void test(String str) {
+ int hashCode = str.hashCode();
+ int id = 0;
+ int foo = one();
+ switch (hashCode) {
+ case 70822: // "Foo".hashCode()
+ if (str.equals("Foo")) {
+ id = foo;
+ }
+ break;
+ case 66547: // "Bar".hashCode()
+ if (str.equals("Bar")) {
+ id = 2;
+ }
+ break;
+ case 66555: // "Baz".hashCode()
+ if (str.equals("Baz")) {
+ id = 3;
+ }
+ break;
+ }
+ switch (id) {
+ case 1:
+ System.out.println("Foo");
+ break;
+ case 2:
+ System.out.println("Bar");
+ break;
+ case 3:
+ System.out.println("Baz");
+ break;
+ default:
+ System.out.println("Qux");
+ break;
+ }
+ }
+
+ @NeverInline
+ static int one() {
+ return 1;
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/switches/SwitchMapInvalidInitTest.java b/src/test/java/com/android/tools/r8/ir/optimize/switches/SwitchMapInvalidInitTest.java
index c8d8086..cb50223 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/switches/SwitchMapInvalidInitTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/switches/SwitchMapInvalidInitTest.java
@@ -35,6 +35,7 @@
testForR8(parameters.getBackend())
.addKeepMainRule(Main.class)
.enableInliningAnnotations()
+ .enableNeverClassInliningAnnotations()
.addInnerClasses(SwitchMapInvalidInitTest.class)
.setMinApi(parameters.getApiLevel())
.compile()
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/switches/SwitchMapInvalidOrdinalTest.java b/src/test/java/com/android/tools/r8/ir/optimize/switches/SwitchMapInvalidOrdinalTest.java
index a779afb..8c84a76 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/switches/SwitchMapInvalidOrdinalTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/switches/SwitchMapInvalidOrdinalTest.java
@@ -45,6 +45,7 @@
+ "com.android.tools.r8.ir.optimize.switches.SwitchMapInvalidOrdinalTest$MyEnum {"
+ " static <fields>; }")
.addInnerClasses(SwitchMapInvalidOrdinalTest.class)
+ .enableNeverClassInliningAnnotations()
.setMinApi(parameters.getApiLevel())
.compile()
.run(parameters.getRuntime(), Main.class)
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/templates/CfUtilityMethodsForCodeOptimizationsTemplates.java b/src/test/java/com/android/tools/r8/ir/optimize/templates/CfUtilityMethodsForCodeOptimizationsTemplates.java
index 8f7c579..c1b1f8b 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/templates/CfUtilityMethodsForCodeOptimizationsTemplates.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/templates/CfUtilityMethodsForCodeOptimizationsTemplates.java
@@ -17,4 +17,12 @@
throw new ClassCastException();
}
}
+
+ public static IncompatibleClassChangeError throwIncompatibleClassChangeError() {
+ throw new IncompatibleClassChangeError();
+ }
+
+ public static NoSuchMethodError throwNoSuchMethodError() {
+ throw new NoSuchMethodError();
+ }
}
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/uninstantiatedtypes/UninstantiatedAnnotatedArgumentsTest.java b/src/test/java/com/android/tools/r8/ir/optimize/uninstantiatedtypes/UninstantiatedAnnotatedArgumentsTest.java
index 035ab54..c6dfe83 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/uninstantiatedtypes/UninstantiatedAnnotatedArgumentsTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/uninstantiatedtypes/UninstantiatedAnnotatedArgumentsTest.java
@@ -54,6 +54,7 @@
public void test() throws Exception {
testForR8(parameters.getBackend())
.addInnerClasses(UninstantiatedAnnotatedArgumentsTest.class)
+ .addConstantArgumentAnnotations()
.addKeepMainRule(TestClass.class)
.addKeepClassRules(Instantiated.class, Uninstantiated.class)
.addKeepAttributes("RuntimeVisibleParameterAnnotations")
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/unusedarguments/UnusedAnnotatedArgumentsTest.java b/src/test/java/com/android/tools/r8/ir/optimize/unusedarguments/UnusedAnnotatedArgumentsTest.java
index 09c0de7..bd1b805 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/unusedarguments/UnusedAnnotatedArgumentsTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/unusedarguments/UnusedAnnotatedArgumentsTest.java
@@ -52,6 +52,7 @@
public void test() throws Exception {
testForR8(parameters.getBackend())
.addInnerClasses(UnusedAnnotatedArgumentsTest.class)
+ .addUnusedArgumentAnnotations()
.addKeepMainRule(TestClass.class)
.addKeepClassRules(Used.class, Unused.class)
.addKeepAttributes("RuntimeVisibleParameterAnnotations")
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/unusedinterfaces/UnusedInterfaceRemovalPackageBoundaryTest.java b/src/test/java/com/android/tools/r8/ir/optimize/unusedinterfaces/UnusedInterfaceRemovalPackageBoundaryTest.java
new file mode 100644
index 0000000..9c4046d
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/ir/optimize/unusedinterfaces/UnusedInterfaceRemovalPackageBoundaryTest.java
@@ -0,0 +1,91 @@
+// 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.optimize.unusedinterfaces;
+
+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;
+
+import com.android.tools.r8.NeverClassInline;
+import com.android.tools.r8.NoVerticalClassMerging;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.ir.optimize.unusedinterfaces.testclasses.UnusedInterfaceRemovalPackageBoundaryTestClasses;
+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;
+
+@RunWith(Parameterized.class)
+public class UnusedInterfaceRemovalPackageBoundaryTest extends TestBase {
+
+ private static final Class<?> I_CLASS = UnusedInterfaceRemovalPackageBoundaryTestClasses.getI();
+ private static final Class<?> J_CLASS = UnusedInterfaceRemovalPackageBoundaryTestClasses.J.class;
+
+ private final TestParameters parameters;
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
+ }
+
+ public UnusedInterfaceRemovalPackageBoundaryTest(TestParameters parameters) {
+ this.parameters = parameters;
+ }
+
+ @Test
+ public void test() throws Exception {
+ testForR8(parameters.getBackend())
+ .addInnerClasses(getClass(), UnusedInterfaceRemovalPackageBoundaryTestClasses.class)
+ .addKeepMainRule(TestClass.class)
+ .addKeepClassRules(I_CLASS)
+ .enableNeverClassInliningAnnotations()
+ .enableNoVerticalClassMergingAnnotations()
+ .setMinApi(parameters.getApiLevel())
+ .noMinification()
+ .compile()
+ .inspect(
+ inspector -> {
+ ClassSubject iClassSubject = inspector.clazz(I_CLASS);
+ assertThat(iClassSubject, isPresent());
+
+ ClassSubject jClassSubject = inspector.clazz(J_CLASS);
+ assertThat(jClassSubject, isPresent());
+
+ ClassSubject kClassSubject = inspector.clazz(K.class);
+ assertThat(kClassSubject, isAbsent());
+
+ ClassSubject aClassSubject = inspector.clazz(A.class);
+ assertThat(aClassSubject, isPresent());
+ assertEquals(1, aClassSubject.getDexProgramClass().getInterfaces().size());
+ assertEquals(
+ jClassSubject.getDexProgramClass().getType(),
+ aClassSubject.getDexProgramClass().getInterfaces().get(0));
+ })
+ .run(parameters.getRuntime(), TestClass.class)
+ .assertSuccessWithOutputLines("A");
+ }
+
+ static class TestClass {
+
+ public static void main(String[] args) {
+ new A();
+ }
+ }
+
+ @NoVerticalClassMerging
+ interface K extends UnusedInterfaceRemovalPackageBoundaryTestClasses.J {}
+
+ @NeverClassInline
+ static class A implements K {
+
+ A() {
+ System.out.println("A");
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/unusedinterfaces/testclasses/UnusedInterfaceRemovalPackageBoundaryTestClasses.java b/src/test/java/com/android/tools/r8/ir/optimize/unusedinterfaces/testclasses/UnusedInterfaceRemovalPackageBoundaryTestClasses.java
new file mode 100644
index 0000000..0b623b2
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/ir/optimize/unusedinterfaces/testclasses/UnusedInterfaceRemovalPackageBoundaryTestClasses.java
@@ -0,0 +1,20 @@
+// 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.unusedinterfaces.testclasses;
+
+import com.android.tools.r8.NoVerticalClassMerging;
+
+public class UnusedInterfaceRemovalPackageBoundaryTestClasses {
+
+ @NoVerticalClassMerging
+ interface I {}
+
+ @NoVerticalClassMerging
+ public interface J extends I {}
+
+ public static Class<?> getI() {
+ return I.class;
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/ir/regalloc/RegisterMoveSchedulerTest.java b/src/test/java/com/android/tools/r8/ir/regalloc/RegisterMoveSchedulerTest.java
index 8d01a11..105008c 100644
--- a/src/test/java/com/android/tools/r8/ir/regalloc/RegisterMoveSchedulerTest.java
+++ b/src/test/java/com/android/tools/r8/ir/regalloc/RegisterMoveSchedulerTest.java
@@ -99,6 +99,17 @@
}
@Override
+ public void replaceCurrentInstructionWithThrow(
+ AppView<?> appView,
+ IRCode code,
+ ListIterator<BasicBlock> blockIterator,
+ Value exceptionValue,
+ Set<BasicBlock> blocksToRemove,
+ Set<Value> affectedValues) {
+ throw new Unimplemented();
+ }
+
+ @Override
public void replaceCurrentInstructionWithThrowNull(
AppView<? extends AppInfoWithClassHierarchy> appView,
IRCode code,
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 293caa8..e4622be 100644
--- a/src/test/java/com/android/tools/r8/kotlin/KotlinClassInlinerTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/KotlinClassInlinerTest.java
@@ -16,6 +16,7 @@
import com.android.tools.r8.KotlinCompilerTool.KotlinCompiler;
import com.android.tools.r8.R8FullTestBuilder;
import com.android.tools.r8.R8TestRunResult;
+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;
@@ -90,6 +91,7 @@
testBuilder
// TODO(jsjeon): Introduce @NeverInline to kotlinR8TestResources
.addKeepRules("-neverinline class * { void test*State*(...); }")
+ .addDontWarnJetBrainsNotNullAnnotation()
.noClassInlining())
.inspect(
inspector -> {
@@ -110,7 +112,8 @@
testBuilder ->
testBuilder
// TODO(jsjeon): Introduce @NeverInline to kotlinR8TestResources
- .addKeepRules("-neverinline class * { void test*State*(...); }"))
+ .addKeepRules("-neverinline class * { void test*State*(...); }")
+ .addDontWarnJetBrainsNotNullAnnotation())
.inspect(
inspector -> {
Predicate<DexType> lambdaCheck = createLambdaCheck(inspector);
@@ -156,6 +159,7 @@
"-neverinline class * { void test*State*(...); }",
"-neverinline class * { void testBigExtraMethod(...); }",
"-neverinline class * { void testBigExtraMethodReturningLambda(...); }")
+ .addDontWarnJetBrainsAnnotations()
.noClassInlining())
.inspect(
inspector -> {
@@ -197,9 +201,10 @@
testBuilder
// TODO(jsjeon): Introduce @NeverInline to kotlinR8TestResources
.addKeepRules(
- "-neverinline class * { void test*State*(...); }",
- "-neverinline class * { void testBigExtraMethod(...); }",
- "-neverinline class * { void testBigExtraMethodReturningLambda(...); }"))
+ "-neverinline class * { void test*State*(...); }",
+ "-neverinline class * { void testBigExtraMethod(...); }",
+ "-neverinline class * { void testBigExtraMethodReturningLambda(...); }")
+ .addDontWarnJetBrainsAnnotations())
.inspect(
inspector -> {
Predicate<DexType> lambdaCheck = createLambdaCheck(inspector);
@@ -280,7 +285,10 @@
public void testDataClass() throws Exception {
assumeTrue("Only work with -allowaccessmodification", allowAccessModification);
final String mainClassName = "class_inliner_data_class.MainKt";
- runTestWithDefaults("class_inliner_data_class", mainClassName)
+ runTestWithDefaults(
+ "class_inliner_data_class",
+ mainClassName,
+ TestShrinkerBuilder::addDontWarnJetBrainsAnnotations)
.inspect(
inspector -> {
ClassSubject clazz = inspector.clazz(mainClassName);
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 ec1793d..e5d0d09 100644
--- a/src/test/java/com/android/tools/r8/kotlin/KotlinClassStaticizerTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/KotlinClassStaticizerTest.java
@@ -74,6 +74,10 @@
return runTest(
folder,
mainClass,
- testBuilder -> testBuilder.noClassInlining().noClassStaticizing(noClassStaticizing));
+ testBuilder ->
+ testBuilder
+ .addDontWarnJetBrainsNotNullAnnotation()
+ .noClassInlining()
+ .noClassStaticizing(noClassStaticizing));
}
}
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 c683b1a..eb1ca57 100644
--- a/src/test/java/com/android/tools/r8/kotlin/KotlinIntrinsicsInlineTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/KotlinIntrinsicsInlineTest.java
@@ -58,6 +58,7 @@
StringUtils.lines(
"-keepclasseswithmembers class " + MAIN + "{", " public static *** *(...);", "}"))
.allowAccessModification(allowAccessModification)
+ .addDontWarnJetBrainsNotNullAnnotation()
.noMinification()
.setMinApi(parameters.getRuntime())
.compile()
@@ -102,6 +103,7 @@
"-keepclasseswithmembers class " + MAIN + "{",
" public static *** " + methodName + "(...);",
"}"))
+ .addDontWarnJetBrainsNotNullAnnotation()
.allowAccessModification(allowAccessModification)
.noMinification()
.setMinApi(parameters.getRuntime())
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 b59d98c..5e25b92 100644
--- a/src/test/java/com/android/tools/r8/kotlin/KotlinUnusedArgumentsInLambdasTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/KotlinUnusedArgumentsInLambdasTest.java
@@ -10,6 +10,7 @@
import static org.junit.Assert.assertTrue;
import com.android.tools.r8.KotlinCompilerTool.KotlinCompiler;
+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;
@@ -36,7 +37,10 @@
@Test
public void testMergingKStyleLambdasAfterUnusedArgumentRemoval() throws Exception {
final String mainClassName = "unused_arg_in_lambdas_kstyle.MainKt";
- runTest("unused_arg_in_lambdas_kstyle", mainClassName)
+ runTest(
+ "unused_arg_in_lambdas_kstyle",
+ mainClassName,
+ TestShrinkerBuilder::addDontWarnJetBrainsAnnotations)
.inspect(
inspector ->
inspector.forAllClasses(
@@ -57,7 +61,10 @@
@Test
public void testMergingJStyleLambdasAfterUnusedArgumentRemoval() throws Exception {
final String mainClassName = "unused_arg_in_lambdas_jstyle.MainKt";
- runTest("unused_arg_in_lambdas_jstyle", mainClassName)
+ runTest(
+ "unused_arg_in_lambdas_jstyle",
+ mainClassName,
+ TestShrinkerBuilder::addDontWarnJetBrainsAnnotations)
.inspect(
inspector ->
inspector.forAllClasses(
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 c358325..6a8e196 100644
--- a/src/test/java/com/android/tools/r8/kotlin/KotlinUnusedSingletonTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/KotlinUnusedSingletonTest.java
@@ -11,6 +11,7 @@
import static org.junit.Assert.assertTrue;
import com.android.tools.r8.KotlinCompilerTool.KotlinCompiler;
+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;
@@ -45,7 +46,10 @@
public void b110196118() throws Exception {
final String mainClassName = "unused_singleton.MainKt";
final String moduleName = "unused_singleton.TestModule";
- runTest("unused_singleton", mainClassName)
+ runTest(
+ "unused_singleton",
+ mainClassName,
+ TestShrinkerBuilder::addDontWarnJetBrainsNotNullAnnotation)
.inspect(
inspector -> {
ClassSubject main = inspector.clazz(mainClassName);
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 e90a8b6..3137d61 100644
--- a/src/test/java/com/android/tools/r8/kotlin/ProcessKotlinReflectionLibTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/ProcessKotlinReflectionLibTest.java
@@ -9,11 +9,11 @@
import com.android.tools.r8.KotlinTestBase;
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;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -38,17 +38,11 @@
this.parameters = parameters;
}
- private void test(Collection<String> rules) throws Exception {
- test(rules, null);
- }
-
- private void test(
- Collection<String> rules, ThrowableConsumer<R8FullTestBuilder> consumer) throws Exception {
+ private void test(ThrowableConsumer<R8FullTestBuilder> consumer) throws Exception {
testForR8(parameters.getBackend())
.addLibraryFiles(
ToolHelper.getMostRecentAndroidJar(), ToolHelper.getKotlinStdlibJar(kotlinc))
.addProgramFiles(ToolHelper.getKotlinReflectJar(kotlinc))
- .addKeepRules(rules)
.addKeepAttributes(ProguardKeepAttributes.SIGNATURE)
.addKeepAttributes(ProguardKeepAttributes.INNER_CLASSES)
.addKeepAttributes(ProguardKeepAttributes.ENCLOSING_METHOD)
@@ -58,54 +52,61 @@
@Test
public void testAsIs() throws Exception {
- test(ImmutableList.of("-dontshrink", "-dontoptimize", "-dontobfuscate"));
+ test(
+ builder ->
+ builder.addDontWarnJetBrains().noMinification().noOptimization().noTreeShaking());
}
@Test
public void testDontShrinkAndDontOptimize() throws Exception {
- test(ImmutableList.of("-dontshrink", "-dontoptimize"));
+ test(builder -> builder.addDontWarnJetBrains().noOptimization().noTreeShaking());
}
@Test
public void testDontShrinkAndDontOptimizeDifferently() throws Exception {
- test(
- ImmutableList.of("-keep,allowobfuscation class **.*KClasses*"),
- tb -> {
- tb.noTreeShaking();
- tb.addOptionsModification(o -> {
- // Randomly choose a couple of optimizations.
- o.enableVerticalClassMerging = false;
- o.enableClassStaticizer = false;
- o.outline.enabled = false;
- });
- });
+ test(
+ builder ->
+ builder
+ .addKeepRules("-keep,allowobfuscation class **.*KClasses*")
+ .addDontWarnJetBrains()
+ .noTreeShaking()
+ .addOptionsModification(
+ o -> {
+ // Randomly choose a couple of optimizations.
+ o.enableVerticalClassMerging = false;
+ o.enableClassStaticizer = false;
+ o.outline.enabled = false;
+ }));
}
@Test
public void testDontShrinkAndDontObfuscate() throws Exception {
- test(ImmutableList.of("-dontshrink", "-dontobfuscate"));
+ test(builder -> builder.addDontWarnJetBrains().noMinification().noTreeShaking());
}
@Test
public void testDontShrink() throws Exception {
- test(ImmutableList.of("-dontshrink"));
+ test(builder -> builder.addDontWarnJetBrains().noTreeShaking());
}
@Test
public void testDontShrinkDifferently() throws Exception {
test(
- ImmutableList.of("-keep,allowobfuscation class **.*KClasses*"),
- tb -> tb.noTreeShaking());
+ builder ->
+ builder
+ .addKeepRules("-keep,allowobfuscation class **.*KClasses*")
+ .addDontWarnJetBrains()
+ .noTreeShaking());
}
@Test
public void testDontOptimize() throws Exception {
- test(ImmutableList.of("-dontoptimize"));
+ test(TestShrinkerBuilder::noOptimization);
}
@Test
public void testDontObfuscate() throws Exception {
- test(ImmutableList.of("-dontobfuscate"));
+ test(TestShrinkerBuilder::noMinification);
}
}
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 7b3fd69..7166aed 100644
--- a/src/test/java/com/android/tools/r8/kotlin/ProcessKotlinStdlibTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/ProcessKotlinStdlibTest.java
@@ -9,6 +9,7 @@
import com.android.tools.r8.KotlinTestBase;
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;
@@ -37,13 +38,12 @@
this.parameters = parameters;
}
- private void test(Collection<String> rules, boolean expectInvalidFoo) throws Exception {
- test(rules, expectInvalidFoo, null);
+ private void test(Collection<String> rules) throws Exception {
+ test(rules, null);
}
private void test(
Collection<String> rules,
- boolean expectInvalidDebugInfo,
ThrowableConsumer<R8FullTestBuilder> consumer)
throws Exception {
testForR8(parameters.getBackend())
@@ -58,56 +58,60 @@
@Test
public void testAsIs() throws Exception {
- test(ImmutableList.of("-dontshrink", "-dontoptimize", "-dontobfuscate"), true);
+ test(
+ ImmutableList.of("-dontshrink", "-dontoptimize", "-dontobfuscate"),
+ TestShrinkerBuilder::addDontWarnJetBrainsAnnotations);
}
@Test
public void testDontShrinkAndDontOptimize() throws Exception {
- test(ImmutableList.of("-dontshrink", "-dontoptimize"), true);
+ test(
+ ImmutableList.of("-dontshrink", "-dontoptimize"),
+ TestShrinkerBuilder::addDontWarnJetBrainsAnnotations);
}
@Test
public void testDontShrinkAndDontOptimizeDifferently() throws Exception {
test(
ImmutableList.of("-keep,allowobfuscation class **.*Exception*"),
- true,
- tb -> {
- tb.noTreeShaking();
- tb.addOptionsModification(
- o -> {
- // Randomly choose a couple of optimizations.
- o.enableClassInlining = false;
- o.enableLambdaMerging = false;
- o.enableValuePropagation = false;
- });
- });
+ 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"), true);
+ test(
+ ImmutableList.of("-dontshrink", "-dontobfuscate"),
+ TestShrinkerBuilder::addDontWarnJetBrainsAnnotations);
}
@Test
public void testDontShrink() throws Exception {
- test(ImmutableList.of("-dontshrink"), true);
+ test(ImmutableList.of("-dontshrink"), TestShrinkerBuilder::addDontWarnJetBrainsAnnotations);
}
@Test
public void testDontShrinkDifferently() throws Exception {
test(
ImmutableList.of("-keep,allowobfuscation class **.*Exception*"),
- true,
- tb -> tb.noTreeShaking());
+ tb -> tb.addDontWarnJetBrainsAnnotations().noTreeShaking());
}
@Test
public void testDontOptimize() throws Exception {
- test(ImmutableList.of("-dontoptimize"), false);
+ test(ImmutableList.of("-dontoptimize"));
}
@Test
public void testDontObfuscate() throws Exception {
- test(ImmutableList.of("-dontobfuscate"), false);
+ test(ImmutableList.of("-dontobfuscate"));
}
}
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 cd5d8b1..882bc22 100644
--- a/src/test/java/com/android/tools/r8/kotlin/R8KotlinAccessorTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/R8KotlinAccessorTest.java
@@ -11,6 +11,7 @@
import com.android.tools.r8.KotlinCompilerTool.KotlinCompiler;
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;
@@ -83,7 +84,10 @@
final TestKotlinCompanionClass testedClass = COMPANION_PROPERTY_CLASS;
String mainClass = addMainToClasspath("properties.CompanionPropertiesKt",
"companionProperties_usePrimitiveProp");
- runTest(PROPERTIES_PACKAGE_NAME, mainClass, R8TestBuilder::noClassStaticizing)
+ runTest(
+ PROPERTIES_PACKAGE_NAME,
+ mainClass,
+ builder -> builder.addDontWarnJetBrainsNotNullAnnotation().noClassStaticizing())
.inspect(
inspector -> {
ClassSubject outerClass =
@@ -116,7 +120,10 @@
final TestKotlinCompanionClass testedClass = COMPANION_PROPERTY_CLASS;
String mainClass = addMainToClasspath("properties.CompanionPropertiesKt",
"companionProperties_usePrivateProp");
- runTest(PROPERTIES_PACKAGE_NAME, mainClass, R8TestBuilder::noClassStaticizing)
+ runTest(
+ PROPERTIES_PACKAGE_NAME,
+ mainClass,
+ builder -> builder.addDontWarnJetBrainsNotNullAnnotation().noClassStaticizing())
.inspect(
inspector -> {
ClassSubject outerClass =
@@ -151,7 +158,10 @@
final TestKotlinCompanionClass testedClass = COMPANION_PROPERTY_CLASS;
String mainClass = addMainToClasspath("properties.CompanionPropertiesKt",
"companionProperties_useInternalProp");
- runTest(PROPERTIES_PACKAGE_NAME, mainClass, R8TestBuilder::noClassStaticizing)
+ runTest(
+ PROPERTIES_PACKAGE_NAME,
+ mainClass,
+ builder -> builder.addDontWarnJetBrainsNotNullAnnotation().noClassStaticizing())
.inspect(
inspector -> {
ClassSubject outerClass =
@@ -185,7 +195,10 @@
final TestKotlinCompanionClass testedClass = COMPANION_PROPERTY_CLASS;
String mainClass = addMainToClasspath("properties.CompanionPropertiesKt",
"companionProperties_usePublicProp");
- runTest(PROPERTIES_PACKAGE_NAME, mainClass, R8TestBuilder::noClassStaticizing)
+ runTest(
+ PROPERTIES_PACKAGE_NAME,
+ mainClass,
+ builder -> builder.addDontWarnJetBrainsNotNullAnnotation().noClassStaticizing())
.inspect(
inspector -> {
ClassSubject outerClass =
@@ -219,7 +232,10 @@
final TestKotlinCompanionClass testedClass = COMPANION_LATE_INIT_PROPERTY_CLASS;
String mainClass = addMainToClasspath("properties.CompanionLateInitPropertiesKt",
"companionLateInitProperties_usePrivateLateInitProp");
- runTest(PROPERTIES_PACKAGE_NAME, mainClass, R8TestBuilder::noClassStaticizing)
+ runTest(
+ PROPERTIES_PACKAGE_NAME,
+ mainClass,
+ builder -> builder.addDontWarnJetBrainsAnnotations().noClassStaticizing())
.inspect(
inspector -> {
ClassSubject outerClass =
@@ -252,7 +268,10 @@
final TestKotlinCompanionClass testedClass = COMPANION_LATE_INIT_PROPERTY_CLASS;
String mainClass = addMainToClasspath("properties.CompanionLateInitPropertiesKt",
"companionLateInitProperties_useInternalLateInitProp");
- runTest(PROPERTIES_PACKAGE_NAME, mainClass)
+ runTest(
+ PROPERTIES_PACKAGE_NAME,
+ mainClass,
+ TestShrinkerBuilder::addDontWarnJetBrainsAnnotations)
.inspect(
inspector -> {
ClassSubject outerClass =
@@ -281,7 +300,10 @@
final TestKotlinCompanionClass testedClass = COMPANION_LATE_INIT_PROPERTY_CLASS;
String mainClass = addMainToClasspath("properties.CompanionLateInitPropertiesKt",
"companionLateInitProperties_usePublicLateInitProp");
- runTest(PROPERTIES_PACKAGE_NAME, mainClass)
+ runTest(
+ PROPERTIES_PACKAGE_NAME,
+ mainClass,
+ TestShrinkerBuilder::addDontWarnJetBrainsAnnotations)
.inspect(
inspector -> {
ClassSubject outerClass =
@@ -359,7 +381,7 @@
String mainClass =
addMainToClasspath(
"accessors.PropertyAccessorForInnerClassKt", "noUseOfPropertyAccessorFromInnerClass");
- runTest("accessors", mainClass)
+ runTest("accessors", mainClass, TestShrinkerBuilder::addDontWarnJetBrainsNotNullAnnotation)
.inspect(
inspector -> {
// Class is removed because the instantiation of the inner class has no side effects.
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 ef13161..3faf486 100644
--- a/src/test/java/com/android/tools/r8/kotlin/R8KotlinDataClassTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/R8KotlinDataClassTest.java
@@ -66,7 +66,10 @@
"dataclass",
mainClassName,
testBuilder ->
- testBuilder.addKeepRules(extraRules).addOptionsModification(disableClassInliner))
+ testBuilder
+ .addKeepRules(extraRules)
+ .addDontWarnJetBrainsAnnotations()
+ .addOptionsModification(disableClassInliner))
.inspect(
inspector -> {
ClassSubject dataClass = checkClassIsKept(inspector, TEST_DATA_CLASS.getClassName());
@@ -107,7 +110,10 @@
"dataclass",
mainClassName,
testBuilder ->
- testBuilder.addKeepRules(extraRules).addOptionsModification(disableClassInliner))
+ testBuilder
+ .addKeepRules(extraRules)
+ .addDontWarnJetBrainsAnnotations()
+ .addOptionsModification(disableClassInliner))
.inspect(
inspector -> {
ClassSubject dataClass = checkClassIsKept(inspector, TEST_DATA_CLASS.getClassName());
@@ -147,7 +153,10 @@
"dataclass",
mainClassName,
testBuilder ->
- testBuilder.addKeepRules(extraRules).addOptionsModification(disableClassInliner))
+ testBuilder
+ .addKeepRules(extraRules)
+ .addDontWarnJetBrainsAnnotations()
+ .addOptionsModification(disableClassInliner))
.inspect(
inspector -> {
ClassSubject dataClass = checkClassIsKept(inspector, TEST_DATA_CLASS.getClassName());
@@ -187,7 +196,10 @@
"dataclass",
mainClassName,
testBuilder ->
- testBuilder.addKeepRules(extraRules).addOptionsModification(disableClassInliner))
+ testBuilder
+ .addKeepRules(extraRules)
+ .addDontWarnJetBrainsAnnotations()
+ .addOptionsModification(disableClassInliner))
.inspect(
inspector -> {
ClassSubject dataClass = checkClassIsKept(inspector, TEST_DATA_CLASS.getClassName());
@@ -207,7 +219,10 @@
"dataclass",
mainClassName,
testBuilder ->
- testBuilder.addKeepRules(extraRules).addOptionsModification(disableClassInliner))
+ testBuilder
+ .addKeepRules(extraRules)
+ .addDontWarnJetBrainsAnnotations()
+ .addOptionsModification(disableClassInliner))
.inspect(
inspector -> {
ClassSubject dataClass = checkClassIsKept(inspector, TEST_DATA_CLASS.getClassName());
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 ea12c12..934ee16 100644
--- a/src/test/java/com/android/tools/r8/kotlin/R8KotlinIntrinsicsTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/R8KotlinIntrinsicsTest.java
@@ -47,7 +47,10 @@
"intrinsics",
"intrinsics.IntrinsicsKt",
testBuilder ->
- testBuilder.addKeepRules(extraRules).noHorizontalClassMerging(Intrinsics.class))
+ testBuilder
+ .addKeepRules(extraRules)
+ .addDontWarnJetBrainsAnnotations()
+ .noHorizontalClassMerging(Intrinsics.class))
.inspect(
inspector -> {
ClassSubject intrinsicsClass =
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 e04585c..d6d2477 100644
--- a/src/test/java/com/android/tools/r8/kotlin/R8KotlinPropertiesTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/R8KotlinPropertiesTest.java
@@ -113,7 +113,10 @@
runTest(
PACKAGE_NAME,
mainClass,
- testBuilder -> testBuilder.addOptionsModification(disableAggressiveClassOptimizations))
+ testBuilder ->
+ testBuilder
+ .addDontWarnJetBrainsNotNullAnnotation()
+ .addOptionsModification(disableAggressiveClassOptimizations))
.inspect(
inspector -> {
checkClassIsRemoved(inspector, MUTABLE_PROPERTY_CLASS.getClassName());
@@ -127,7 +130,10 @@
runTest(
PACKAGE_NAME,
mainClass,
- testBuilder -> testBuilder.addOptionsModification(disableAggressiveClassOptimizations))
+ testBuilder ->
+ testBuilder
+ .addDontWarnJetBrainsNotNullAnnotation()
+ .addOptionsModification(disableAggressiveClassOptimizations))
.inspect(
inspector -> {
ClassSubject classSubject =
@@ -156,7 +162,10 @@
runTest(
PACKAGE_NAME,
mainClass,
- testBuilder -> testBuilder.addOptionsModification(disableAggressiveClassOptimizations))
+ testBuilder ->
+ testBuilder
+ .addDontWarnJetBrainsNotNullAnnotation()
+ .addOptionsModification(disableAggressiveClassOptimizations))
.inspect(
inspector -> {
ClassSubject classSubject =
@@ -184,7 +193,10 @@
runTest(
PACKAGE_NAME,
mainClass,
- testBuilder -> testBuilder.addOptionsModification(disableAggressiveClassOptimizations))
+ testBuilder ->
+ testBuilder
+ .addDontWarnJetBrainsNotNullAnnotation()
+ .addOptionsModification(disableAggressiveClassOptimizations))
.inspect(
inspector -> {
ClassSubject classSubject =
@@ -212,7 +224,10 @@
runTest(
PACKAGE_NAME,
mainClass,
- testBuilder -> testBuilder.addOptionsModification(disableAggressiveClassOptimizations))
+ testBuilder ->
+ testBuilder
+ .addDontWarnJetBrainsNotNullAnnotation()
+ .addOptionsModification(disableAggressiveClassOptimizations))
.inspect(
inspector -> {
ClassSubject classSubject =
@@ -240,7 +255,10 @@
runTest(
PACKAGE_NAME,
mainClass,
- testBuilder -> testBuilder.addOptionsModification(disableAggressiveClassOptimizations))
+ testBuilder ->
+ testBuilder
+ .addDontWarnJetBrainsNotNullAnnotation()
+ .addOptionsModification(disableAggressiveClassOptimizations))
.inspect(
inspector -> {
ClassSubject classSubject =
@@ -283,7 +301,10 @@
runTest(
PACKAGE_NAME,
mainClass,
- testBuilder -> testBuilder.addOptionsModification(disableAggressiveClassOptimizations))
+ testBuilder ->
+ testBuilder
+ .addDontWarnJetBrainsAnnotations()
+ .addOptionsModification(disableAggressiveClassOptimizations))
.inspect(
inspector -> {
ClassSubject classSubject =
@@ -310,7 +331,10 @@
runTest(
PACKAGE_NAME,
mainClass,
- testBuilder -> testBuilder.addOptionsModification(disableAggressiveClassOptimizations))
+ testBuilder ->
+ testBuilder
+ .addDontWarnJetBrainsAnnotations()
+ .addOptionsModification(disableAggressiveClassOptimizations))
.inspect(
inspector -> {
ClassSubject classSubject =
@@ -335,7 +359,10 @@
runTest(
PACKAGE_NAME,
mainClass,
- testBuilder -> testBuilder.addOptionsModification(disableAggressiveClassOptimizations))
+ testBuilder ->
+ testBuilder
+ .addDontWarnJetBrainsAnnotations()
+ .addOptionsModification(disableAggressiveClassOptimizations))
.inspect(
inspector -> {
ClassSubject classSubject =
@@ -358,7 +385,10 @@
runTest(
PACKAGE_NAME,
mainClass,
- testBuilder -> testBuilder.addOptionsModification(disableAggressiveClassOptimizations))
+ testBuilder ->
+ testBuilder
+ .addDontWarnJetBrainsAnnotations()
+ .addOptionsModification(disableAggressiveClassOptimizations))
.inspect(
inspector -> {
ClassSubject classSubject =
@@ -426,7 +456,10 @@
runTest(
PACKAGE_NAME,
mainClass,
- testBuilder -> testBuilder.addOptionsModification(disableAggressiveClassOptimizations))
+ testBuilder ->
+ testBuilder
+ .addDontWarnJetBrainsNotNullAnnotation()
+ .addOptionsModification(disableAggressiveClassOptimizations))
.inspect(
inspector -> {
ClassSubject outerClass =
@@ -459,7 +492,10 @@
runTest(
PACKAGE_NAME,
mainClass,
- testBuilder -> testBuilder.addOptionsModification(disableAggressiveClassOptimizations))
+ testBuilder ->
+ testBuilder
+ .addDontWarnJetBrainsNotNullAnnotation()
+ .addOptionsModification(disableAggressiveClassOptimizations))
.inspect(
inspector -> {
ClassSubject outerClass =
@@ -498,7 +534,10 @@
runTest(
PACKAGE_NAME,
mainClass,
- testBuilder -> testBuilder.addOptionsModification(disableAggressiveClassOptimizations))
+ testBuilder ->
+ testBuilder
+ .addDontWarnJetBrainsNotNullAnnotation()
+ .addOptionsModification(disableAggressiveClassOptimizations))
.inspect(
inspector -> {
ClassSubject outerClass =
@@ -531,7 +570,10 @@
runTest(
PACKAGE_NAME,
mainClass,
- testBuilder -> testBuilder.addOptionsModification(disableAggressiveClassOptimizations))
+ testBuilder ->
+ testBuilder
+ .addDontWarnJetBrainsNotNullAnnotation()
+ .addOptionsModification(disableAggressiveClassOptimizations))
.inspect(
inspector -> {
ClassSubject outerClass =
@@ -566,7 +608,10 @@
runTest(
PACKAGE_NAME,
mainClass,
- testBuilder -> testBuilder.addOptionsModification(disableAggressiveClassOptimizations))
+ testBuilder ->
+ testBuilder
+ .addDontWarnJetBrainsAnnotations()
+ .addOptionsModification(disableAggressiveClassOptimizations))
.inspect(
inspector -> {
ClassSubject outerClass =
@@ -603,7 +648,10 @@
runTest(
PACKAGE_NAME,
mainClass,
- testBuilder -> testBuilder.addOptionsModification(disableAggressiveClassOptimizations))
+ testBuilder ->
+ testBuilder
+ .addDontWarnJetBrainsAnnotations()
+ .addOptionsModification(disableAggressiveClassOptimizations))
.inspect(
inspector -> {
ClassSubject outerClass =
@@ -631,7 +679,10 @@
runTest(
PACKAGE_NAME,
mainClass,
- testBuilder -> testBuilder.addOptionsModification(disableAggressiveClassOptimizations))
+ testBuilder ->
+ testBuilder
+ .addDontWarnJetBrainsAnnotations()
+ .addOptionsModification(disableAggressiveClassOptimizations))
.inspect(
inspector -> {
ClassSubject outerClass =
@@ -659,7 +710,10 @@
runTest(
PACKAGE_NAME,
mainClass,
- testBuilder -> testBuilder.addOptionsModification(disableAggressiveClassOptimizations))
+ testBuilder ->
+ testBuilder
+ .addDontWarnJetBrainsNotNullAnnotation()
+ .addOptionsModification(disableAggressiveClassOptimizations))
.inspect(
inspector -> {
ClassSubject objectClass = checkClassIsKept(inspector, testedClass.getClassName());
@@ -697,7 +751,10 @@
runTest(
PACKAGE_NAME,
mainClass,
- testBuilder -> testBuilder.addOptionsModification(disableAggressiveClassOptimizations))
+ testBuilder ->
+ testBuilder
+ .addDontWarnJetBrainsNotNullAnnotation()
+ .addOptionsModification(disableAggressiveClassOptimizations))
.inspect(
inspector -> {
ClassSubject objectClass = checkClassIsKept(inspector, testedClass.getClassName());
@@ -729,7 +786,10 @@
runTest(
PACKAGE_NAME,
mainClass,
- testBuilder -> testBuilder.addOptionsModification(disableAggressiveClassOptimizations))
+ testBuilder ->
+ testBuilder
+ .addDontWarnJetBrainsNotNullAnnotation()
+ .addOptionsModification(disableAggressiveClassOptimizations))
.inspect(
inspector -> {
ClassSubject objectClass = checkClassIsKept(inspector, testedClass.getClassName());
@@ -768,7 +828,10 @@
runTest(
PACKAGE_NAME,
mainClass,
- testBuilder -> testBuilder.addOptionsModification(disableAggressiveClassOptimizations))
+ testBuilder ->
+ testBuilder
+ .addDontWarnJetBrainsNotNullAnnotation()
+ .addOptionsModification(disableAggressiveClassOptimizations))
.inspect(
inspector -> {
ClassSubject objectClass = checkClassIsKept(inspector, testedClass.getClassName());
@@ -807,7 +870,10 @@
runTest(
PACKAGE_NAME,
mainClass,
- testBuilder -> testBuilder.addOptionsModification(disableAggressiveClassOptimizations))
+ testBuilder ->
+ testBuilder
+ .addDontWarnJetBrainsAnnotations()
+ .addOptionsModification(disableAggressiveClassOptimizations))
.inspect(
inspector -> {
ClassSubject objectClass = checkClassIsKept(inspector, testedClass.getClassName());
@@ -839,7 +905,10 @@
runTest(
PACKAGE_NAME,
mainClass,
- testBuilder -> testBuilder.addOptionsModification(disableAggressiveClassOptimizations))
+ testBuilder ->
+ testBuilder
+ .addDontWarnJetBrainsAnnotations()
+ .addOptionsModification(disableAggressiveClassOptimizations))
.inspect(
inspector -> {
ClassSubject objectClass = checkClassIsKept(inspector, testedClass.getClassName());
@@ -865,7 +934,10 @@
runTest(
PACKAGE_NAME,
mainClass,
- testBuilder -> testBuilder.addOptionsModification(disableAggressiveClassOptimizations))
+ testBuilder ->
+ testBuilder
+ .addDontWarnJetBrainsAnnotations()
+ .addOptionsModification(disableAggressiveClassOptimizations))
.inspect(
inspector -> {
ClassSubject objectClass = checkClassIsKept(inspector, testedClass.getClassName());
@@ -891,7 +963,10 @@
runTest(
PACKAGE_NAME,
mainClass,
- testBuilder -> testBuilder.addOptionsModification(disableAggressiveClassOptimizations))
+ testBuilder ->
+ testBuilder
+ .addDontWarnJetBrainsNotNullAnnotation()
+ .addOptionsModification(disableAggressiveClassOptimizations))
.inspect(
inspector -> {
ClassSubject objectClass = checkClassIsKept(inspector, testedClass.getClassName());
@@ -922,7 +997,10 @@
runTest(
PACKAGE_NAME,
mainClass,
- testBuilder -> testBuilder.addOptionsModification(disableAggressiveClassOptimizations))
+ testBuilder ->
+ testBuilder
+ .addDontWarnJetBrainsNotNullAnnotation()
+ .addOptionsModification(disableAggressiveClassOptimizations))
.inspect(
inspector -> {
ClassSubject objectClass = checkClassIsKept(inspector, testedClass.getClassName());
@@ -953,7 +1031,10 @@
runTest(
PACKAGE_NAME,
mainClass,
- testBuilder -> testBuilder.addOptionsModification(disableAggressiveClassOptimizations))
+ testBuilder ->
+ testBuilder
+ .addDontWarnJetBrainsNotNullAnnotation()
+ .addOptionsModification(disableAggressiveClassOptimizations))
.inspect(
inspector -> {
ClassSubject objectClass = checkClassIsKept(inspector, testedClass.getClassName());
@@ -986,7 +1067,10 @@
runTest(
PACKAGE_NAME,
mainClass,
- testBuilder -> testBuilder.addOptionsModification(disableAggressiveClassOptimizations))
+ testBuilder ->
+ testBuilder
+ .addDontWarnJetBrainsNotNullAnnotation()
+ .addOptionsModification(disableAggressiveClassOptimizations))
.inspect(
inspector -> {
ClassSubject objectClass = checkClassIsKept(inspector, testedClass.getClassName());
@@ -1018,7 +1102,10 @@
runTest(
PACKAGE_NAME,
mainClass,
- testBuilder -> testBuilder.addOptionsModification(disableAggressiveClassOptimizations))
+ testBuilder ->
+ testBuilder
+ .addDontWarnJetBrainsAnnotations()
+ .addOptionsModification(disableAggressiveClassOptimizations))
.inspect(
inspector -> {
ClassSubject fileClass = checkClassIsKept(inspector, testedClass.getClassName());
@@ -1049,7 +1136,10 @@
runTest(
PACKAGE_NAME,
mainClass,
- testBuilder -> testBuilder.addOptionsModification(disableAggressiveClassOptimizations))
+ testBuilder ->
+ testBuilder
+ .addDontWarnJetBrainsAnnotations()
+ .addOptionsModification(disableAggressiveClassOptimizations))
.inspect(
inspector -> {
ClassSubject objectClass = checkClassIsKept(inspector, testedClass.getClassName());
@@ -1077,7 +1167,10 @@
runTest(
PACKAGE_NAME,
mainClass,
- testBuilder -> testBuilder.addOptionsModification(disableAggressiveClassOptimizations))
+ testBuilder ->
+ testBuilder
+ .addDontWarnJetBrainsAnnotations()
+ .addOptionsModification(disableAggressiveClassOptimizations))
.inspect(
inspector -> {
ClassSubject objectClass = checkClassIsKept(inspector, testedClass.getClassName());
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 70fd115..64e3a80 100644
--- a/src/test/java/com/android/tools/r8/kotlin/SimplifyIfNotNullKotlinTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/SimplifyIfNotNullKotlinTest.java
@@ -43,7 +43,10 @@
final String mainClassName = ex1.getClassName();
final String extraRules = neverInlineMethod(mainClassName, testMethodSignature);
- runTest(FOLDER, mainClassName, testBuilder -> testBuilder.addKeepRules(extraRules))
+ runTest(
+ FOLDER,
+ mainClassName,
+ testBuilder -> testBuilder.addKeepRules(extraRules).addDontWarnJetBrainsAnnotations())
.inspect(
inspector -> {
ClassSubject clazz = checkClassIsKept(inspector, ex1.getClassName());
@@ -67,7 +70,10 @@
final String mainClassName = ex2.getClassName();
final String extraRules = neverInlineMethod(mainClassName, testMethodSignature);
- runTest(FOLDER, mainClassName, testBuilder -> testBuilder.addKeepRules(extraRules))
+ runTest(
+ FOLDER,
+ mainClassName,
+ testBuilder -> testBuilder.addKeepRules(extraRules).addDontWarnJetBrainsAnnotations())
.inspect(
inspector -> {
ClassSubject clazz = checkClassIsKept(inspector, ex2.getClassName());
@@ -91,7 +97,10 @@
final String mainClassName = ex3.getClassName();
final String extraRules = neverInlineMethod(mainClassName, testMethodSignature);
- runTest(FOLDER, mainClassName, testBuilder -> testBuilder.addKeepRules(extraRules))
+ runTest(
+ FOLDER,
+ mainClassName,
+ testBuilder -> testBuilder.addKeepRules(extraRules).addDontWarnJetBrainsAnnotations())
.inspect(
inspector -> {
ClassSubject clazz = checkClassIsKept(inspector, ex3.getClassName());
diff --git a/src/test/java/com/android/tools/r8/kotlin/TestKotlinDataClass.java b/src/test/java/com/android/tools/r8/kotlin/TestKotlinDataClass.java
index 62cda72..0426d41 100644
--- a/src/test/java/com/android/tools/r8/kotlin/TestKotlinDataClass.java
+++ b/src/test/java/com/android/tools/r8/kotlin/TestKotlinDataClass.java
@@ -36,18 +36,20 @@
}
public MemberNaming.MethodSignature getCopySignature() {
- List<String> propertiesTypes = properties.values().stream()
- .sorted(Comparator.comparingInt(p -> p.getIndex()))
- .map(p -> p.getType())
- .collect(Collectors.toList());
+ List<String> propertiesTypes =
+ properties.values().stream()
+ .sorted(Comparator.comparingInt(KotlinProperty::getIndex))
+ .map(KotlinProperty::getType)
+ .collect(Collectors.toList());
return new MemberNaming.MethodSignature("copy", className, propertiesTypes);
}
public MemberNaming.MethodSignature getCopyDefaultSignature() {
- List<String> propertiesTypes = properties.values().stream()
- .sorted(Comparator.comparingInt(p -> p.getIndex()))
- .map(p -> p.getType())
- .collect(Collectors.toList());
+ List<String> propertiesTypes =
+ properties.values().stream()
+ .sorted(Comparator.comparingInt(KotlinProperty::getIndex))
+ .map(KotlinProperty::getType)
+ .collect(Collectors.toList());
List<String> copyDefaultParameterTypes = new ArrayList<>(propertiesTypes.size() + 3);
copyDefaultParameterTypes.add(className);
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 9ae5aed..4a05b76 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
@@ -80,10 +80,14 @@
.addKeepAllAttributes()
// The BASE_LIBRARY contains proguard rules that do not match.
.allowUnusedProguardConfigurationRules()
- .addKeepRules(
- "-dontwarn reactor.blockhound.integration.BlockHoundIntegration",
- "-dontwarn org.junit.runners.model.Statement",
- "-dontwarn org.junit.rules.TestRule")
+ .addDontWarn(
+ "edu.umd.cs.findbugs.annotations.SuppressFBWarnings",
+ "reactor.blockhound.BlockHound$Builder",
+ "reactor.blockhound.integration.BlockHoundIntegration",
+ "org.junit.rules.TestRule",
+ "org.junit.runner.Description",
+ "org.junit.runners.model.Statement",
+ "org.junit.runners.model.TestTimedOutException")
.compile()
.inspect(inspector -> assertEqualMetadata(new CodeInspector(BASE_LIBRARY), inspector))
.writeToZip();
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 b9baae8..aea188c 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
@@ -55,6 +55,7 @@
.addHorizontallyMergedLambdaClassesInspector(
inspector -> inspector.assertMerged(Lambda1.class, Lambda2.class))
.addEnumUnboxingInspector(inspector -> inspector.assertUnboxed(EnumUnboxingCandidate.class))
+ .addDontWarnJetBrainsNotNullAnnotation()
.enableInliningAnnotations()
.enableNeverClassInliningAnnotations()
.enableNoHorizontalClassMergingAnnotations()
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 1b6d4c0..4f78df2 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
@@ -57,6 +57,7 @@
.addLibraryFiles(ToolHelper.getKotlinStdlibJar(kotlinc))
.addProgramFiles(ktClasses)
.addKeepMainRule("**.B143165163Kt")
+ .addDontWarnJetBrainsNotNullAnnotation()
.setMinApi(parameters.getApiLevel())
.compile()
.addRunClasspathFiles(ToolHelper.getKotlinStdlibJar(kotlinc))
@@ -80,6 +81,7 @@
.addProgramFiles(ToolHelper.getKotlinStdlibJar(kotlinc))
.addProgramFiles(ktClasses)
.addKeepMainRule("**.B143165163Kt")
+ .addDontWarnJetBrainsNotNullAnnotation()
.allowDiagnosticWarningMessages()
.setMinApi(parameters.getApiLevel())
.compile()
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 e7c4762..9cee958 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
@@ -45,6 +45,7 @@
.setMode(CompilationMode.DEBUG)
.addProgramFiles(compiledJars.getForConfiguration(kotlinc, KotlinTargetVersion.JAVA_6))
.addProgramFiles(getJavaJarFile(FOLDER))
+ .addDontWarnJetBrainsNotNullAnnotation()
.setMinApi(parameters.getApiLevel())
.addKeepMainRule(MAIN_CLASS)
.allowDiagnosticWarningMessages()
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
index b9e0546..5a23bd3 100644
--- a/src/test/java/com/android/tools/r8/kotlin/lambda/KotlinLambdaMergingTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/lambda/KotlinLambdaMergingTest.java
@@ -306,6 +306,7 @@
.addKeepRules(
"-keepunusedarguments class * extends kotlin.jvm.internal.Lambda {"
+ " invoke(int, short); }")
+ .addDontWarnJetBrainsNotNullAnnotation()
.addOptionsModification(this::configure))
.inspect(
inspector -> {
@@ -356,7 +357,10 @@
runTest(
"lambdas_kstyle_captures",
mainClassName,
- testBuilder -> testBuilder.addOptionsModification(this::configure))
+ testBuilder ->
+ testBuilder
+ .addDontWarnJetBrainsAnnotations()
+ .addOptionsModification(this::configure))
.inspect(
inspector -> {
if (enableUnusedInterfaceRemoval) {
@@ -390,7 +394,10 @@
runTest(
"lambdas_kstyle_generics",
mainClassName,
- testBuilder -> testBuilder.addOptionsModification(this::configure))
+ testBuilder ->
+ testBuilder
+ .addDontWarnJetBrainsAnnotations()
+ .addOptionsModification(this::configure))
.inspect(
inspector -> {
if (enableUnusedInterfaceRemoval) {
@@ -422,6 +429,7 @@
mainClassName,
testBuilder ->
testBuilder
+ .addDontWarnJetBrainsAnnotations()
.addKeepAttributeInnerClassesAndEnclosingMethod()
.addOptionsModification(this::configure))
.inspect(
@@ -458,6 +466,7 @@
// KEEP_SIGNATURE_INNER_ENCLOSING,
testBuilder ->
testBuilder
+ .addDontWarnJetBrainsAnnotations()
.addKeepAttributeInnerClassesAndEnclosingMethod()
.addKeepAttributeSignature()
.addOptionsModification(this::configure))
@@ -494,7 +503,10 @@
runTest(
"lambdas_jstyle_trivial",
mainClassName,
- testBuilder -> testBuilder.addOptionsModification(this::configure))
+ testBuilder ->
+ testBuilder
+ .addDontWarnJetBrainsAnnotations()
+ .addOptionsModification(this::configure))
.inspect(
inspector -> {
Verifier verifier = new Verifier(inspector);
@@ -545,7 +557,10 @@
"lambdas_singleton",
mainClassName,
testBuilder ->
- testBuilder.addOptionsModification(this::configure).noHorizontalClassMerging())
+ testBuilder
+ .addDontWarnJetBrainsAnnotations()
+ .addOptionsModification(this::configure)
+ .noHorizontalClassMerging())
.inspect(
inspector -> {
Verifier verifier = new Verifier(inspector);
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 b1b38b9..b8fb366 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
@@ -35,11 +35,13 @@
"reprocess_merged_lambdas_kstyle",
mainClassName,
testBuilder ->
- testBuilder.addOptionsModification(
- options -> {
- options.enableInlining = true;
- options.enableClassInlining = true;
- options.enableLambdaMerging = true;
- }));
+ testBuilder
+ .addDontWarnJetBrainsNotNullAnnotation()
+ .addOptionsModification(
+ options -> {
+ options.enableInlining = true;
+ options.enableClassInlining = true;
+ options.enableLambdaMerging = true;
+ }));
}
}
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 1dcdcfb..5032ea2 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
@@ -35,7 +35,8 @@
"lambdas_jstyle_runnable",
mainClassName,
testBuilder ->
- testBuilder.addOptionsModification(
- options -> options.inliningInstructionAllowance = 3));
+ testBuilder
+ .addDontWarnJetBrainsAnnotations()
+ .addOptionsModification(options -> options.inliningInstructionAllowance = 3));
}
}
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 0dd8a62..e9f4e46 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
@@ -139,6 +139,7 @@
.setProgramConsumer(new ArchiveConsumer(featureCode, false))
.build())
.allowDiagnosticWarningMessages()
+ .addDontWarnJetBrainsNotNullAnnotation()
.compile()
.assertAllWarningMessagesMatch(
equalTo("Resource 'META-INF/MANIFEST.MF' already exists."))
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 57cdae7..c33d03b 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
@@ -94,6 +94,7 @@
inspector.assertNoClassesMerged();
}
})
+ .addDontWarnJetBrainsNotNullAnnotation()
.compile();
Path path = compileResult.writeToZip();
compileResult
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 c261c25..663c081 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
@@ -71,6 +71,7 @@
options -> options.horizontalClassMergerOptions().disableKotlinLambdaMerging())
.setMinApi(parameters.getApiLevel())
.addKeepMainRule(PKG_NAME + ".SimpleKt")
+ .addDontWarnJetBrainsNotNullAnnotation()
.applyIf(
splitGroup,
b ->
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 31b489a..b4fd614 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
@@ -86,6 +86,7 @@
.addKeepAllClassesRuleWithAllowObfuscation()
.addKeepRules("-keep class " + PKG_LIB + ".LibKt { *; }")
.addKeepRules("-keep class kotlin.Metadata { *; }")
+ .addDontWarnJetBrainsAnnotations()
.applyIf(keepUnit, b -> b.addKeepRules("-keep class kotlin.Unit { *; }"))
.addKeepAttributes(ProguardKeepAttributes.RUNTIME_VISIBLE_ANNOTATIONS)
.allowDiagnosticWarningMessages()
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 ea9ed33..b6d7701 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
@@ -104,7 +104,6 @@
.addKeepRules("-keepclassmembers,allowaccessmodification class **.Lib$Comp { *; }")
.addKeepRules("-keep,allowaccessmodification,allowobfuscation class **.Lib$Comp { *; }")
.addKeepRules("-keep,allowaccessmodification,allowobfuscation class **.LibKt { *; }")
- .addKeepRules("-allowaccessmodification")
.addApplyMapping(
StringUtils.lines(
PKG_LIB + ".Lib -> " + PKG_LIB + ".LibReference:",
@@ -115,6 +114,9 @@
" void staticPrivate() -> staticPrivateReference",
" void staticInternal() -> staticInternalReference"))
.addKeepRuntimeVisibleAnnotations()
+ .addDontWarnJetBrainsNotNullAnnotation()
+ .addDontWarnKotlin()
+ .allowAccessModification()
.compile()
.inspect(this::inspect)
.writeToZip();
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 428103f..a285149 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
@@ -121,6 +121,8 @@
ProguardKeepAttributes.RUNTIME_VISIBLE_ANNOTATIONS,
ProguardKeepAttributes.RUNTIME_VISIBLE_PARAMETER_ANNOTATIONS,
ProguardKeepAttributes.RUNTIME_VISIBLE_TYPE_ANNOTATIONS)
+ .addDontWarnJetBrainsNotNullAnnotation()
+ .addDontWarnKotlin()
.compile()
.inspect(this::inspect)
.writeToZip();
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 0ff40ff..8db1944 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
@@ -76,6 +76,7 @@
ProguardKeepAttributes.SIGNATURE,
ProguardKeepAttributes.INNER_CLASSES,
ProguardKeepAttributes.ENCLOSING_METHOD)
+ .addDontWarnJetBrainsNotNullAnnotation()
.compile()
.inspect(this::inspect)
.writeToZip();
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 2c2ae83..4e7d224 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
@@ -101,6 +101,7 @@
ProguardKeepAttributes.SIGNATURE,
ProguardKeepAttributes.INNER_CLASSES,
ProguardKeepAttributes.ENCLOSING_METHOD)
+ .addDontWarnJetBrainsAnnotations()
.compile()
.inspect(this::inspect)
.writeToZip();
@@ -157,6 +158,7 @@
ProguardKeepAttributes.SIGNATURE,
ProguardKeepAttributes.INNER_CLASSES,
ProguardKeepAttributes.ENCLOSING_METHOD)
+ .addDontWarnJetBrainsAnnotations()
.compile()
.writeToZip();
Path main =
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 56fd483..065d63f 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
@@ -68,6 +68,8 @@
.addProgramFiles(libJars.getForConfiguration(kotlinc, targetVersion))
.addKeepAllClassesRule()
.addKeepAllAttributes()
+ .addDontWarnJetBrainsNotNullAnnotation()
+ .addDontWarnKotlin()
.compile()
.writeToZip();
Path output =
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 b8c5ac2..15f68ea 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
@@ -68,6 +68,8 @@
.addProgramFiles(libJars.getForConfiguration(kotlinc, targetVersion))
.addKeepAllClassesRule()
.addKeepAllAttributes()
+ .addDontWarnJetBrainsNotNullAnnotation()
+ .addDontWarnKotlin()
.compile()
.writeToZip();
Path output =
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 d71248e..b43f846 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
@@ -71,6 +71,8 @@
.addProgramFiles(jars.getForConfiguration(kotlinc, targetVersion))
.addKeepAllClassesRule()
.addKeepAttributes(ProguardKeepAttributes.RUNTIME_VISIBLE_ANNOTATIONS)
+ .addDontWarnJetBrainsAnnotations()
+ .addDontWarnKotlin()
.compile()
.inspect(
inspector ->
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 15112f6..2ccbb3a 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
@@ -51,6 +51,7 @@
.addKeepKotlinMetadata()
.addKeepRules(StringUtils.joinLines("-if class *.Metadata", "-keep class <1>.io.** { *; }"))
.addKeepAttributes(ProguardKeepAttributes.RUNTIME_VISIBLE_ANNOTATIONS)
+ .addDontWarnJetBrainsAnnotations()
.compile()
.inspect(this::inspect);
}
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 fb4dab9..feca0f8 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
@@ -53,6 +53,7 @@
.addKeepAllClassesRule()
.addKeepKotlinMetadata()
.addKeepAttributes(ProguardKeepAttributes.RUNTIME_VISIBLE_ANNOTATIONS)
+ .addDontWarnJetBrainsAnnotations()
.compile()
.inspect(this::inspectEmptyValuesAreNotPresent);
}
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 ecaea79..fbfc10f 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
@@ -88,6 +88,7 @@
ProguardKeepAttributes.SIGNATURE,
ProguardKeepAttributes.INNER_CLASSES,
ProguardKeepAttributes.ENCLOSING_METHOD)
+ .addDontWarnJetBrainsNotNullAnnotation()
.compile()
.inspect(this::inspect)
.writeToZip();
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 09d3af7..c7b9ea6 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
@@ -94,6 +94,7 @@
// to be called with Kotlin syntax from other kotlin code.
.addKeepRules("-keep class **.ImplKt { <methods>; }")
.addKeepAttributes(ProguardKeepAttributes.RUNTIME_VISIBLE_ANNOTATIONS)
+ .addDontWarnJetBrainsNotNullAnnotation()
.compile()
.inspect(this::inspectRenamed)
.writeToZip();
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 d025713..bdcb905 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
@@ -93,6 +93,7 @@
// To keep ...$Companion structure
.addKeepAttributes(ProguardKeepAttributes.INNER_CLASSES)
.addKeepAttributes(ProguardKeepAttributes.ENCLOSING_METHOD)
+ .addDontWarnJetBrainsNotNullAnnotation()
.compile()
.inspect(codeInspector -> inspect(codeInspector, true))
.writeToZip();
@@ -135,6 +136,7 @@
// To keep ...$Companion structure
.addKeepAttributes(ProguardKeepAttributes.INNER_CLASSES)
.addKeepAttributes(ProguardKeepAttributes.ENCLOSING_METHOD)
+ .addDontWarnJetBrainsNotNullAnnotation()
.compile()
.inspect(codeInspector -> inspect(codeInspector, false))
.writeToZip();
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 ac363d4..f9124bd 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
@@ -92,6 +92,7 @@
.addKeepAttributes(ProguardKeepAttributes.SIGNATURE)
.addKeepAttributes(ProguardKeepAttributes.INNER_CLASSES)
.addKeepAttributes(ProguardKeepAttributes.ENCLOSING_METHOD)
+ .addDontWarnJetBrainsNotNullAnnotation()
.compile()
.inspect(this::inspectMerged)
.writeToZip();
@@ -146,6 +147,7 @@
.addKeepAttributes(ProguardKeepAttributes.SIGNATURE)
.addKeepAttributes(ProguardKeepAttributes.INNER_CLASSES)
.addKeepAttributes(ProguardKeepAttributes.ENCLOSING_METHOD)
+ .addDontWarnJetBrainsNotNullAnnotation()
.compile()
.inspect(this::inspectRenamed)
.writeToZip();
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 327ec34..c78cea1 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
@@ -88,6 +88,7 @@
// to be called with Kotlin syntax from other kotlin code.
.addKeepRules("-keep class **.BKt { <methods>; }")
.addKeepAttributes(ProguardKeepAttributes.RUNTIME_VISIBLE_ANNOTATIONS)
+ .addDontWarnJetBrainsNotNullAnnotation()
.compile()
.inspect(this::inspectMerged)
.writeToZip();
@@ -151,6 +152,7 @@
// to be called with Kotlin syntax from other kotlin code.
.addKeepRules("-keep class **.BKt { <methods>; }")
.addKeepAttributes(ProguardKeepAttributes.RUNTIME_VISIBLE_ANNOTATIONS)
+ .addDontWarnJetBrainsNotNullAnnotation()
.compile()
.inspect(this::inspectRenamed)
.writeToZip();
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 91ed797..5c8468d 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
@@ -86,6 +86,7 @@
// Keep the BKt method, which will be called from other kotlin code.
.addKeepRules("-keep class **.BKt { <methods>; }")
.addKeepAttributes(ProguardKeepAttributes.RUNTIME_VISIBLE_ANNOTATIONS)
+ .addDontWarnJetBrainsNotNullAnnotation()
.compile()
.inspect(this::inspectMerged)
.writeToZip();
@@ -147,6 +148,7 @@
// Keep the BKt method, which will be called from other kotlin code.
.addKeepRules("-keep class **.BKt { <methods>; }")
.addKeepAttributes(ProguardKeepAttributes.RUNTIME_VISIBLE_ANNOTATIONS)
+ .addDontWarnJetBrainsNotNullAnnotation()
.compile()
.inspect(this::inspectRenamed)
.writeToZip();
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 c9210a1..db348d0 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
@@ -87,6 +87,7 @@
.addKeepAttributes(ProguardKeepAttributes.SIGNATURE)
.addKeepAttributes(ProguardKeepAttributes.INNER_CLASSES)
.addKeepAttributes(ProguardKeepAttributes.ENCLOSING_METHOD)
+ .addDontWarnJetBrainsNotNullAnnotation()
.compile()
.inspect(this::inspect)
.writeToZip();
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 a65c4b9..65207dd 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
@@ -89,6 +89,7 @@
.addKeepAttributes(ProguardKeepAttributes.SIGNATURE)
.addKeepAttributes(ProguardKeepAttributes.INNER_CLASSES)
.addKeepAttributes(ProguardKeepAttributes.ENCLOSING_METHOD)
+ .addDontWarnJetBrainsNotNullAnnotation()
.compile()
.inspect(this::inspect)
.writeToZip();
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 e635091..f3c59d9 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
@@ -87,7 +87,6 @@
Path out =
testForR8(parameters.getBackend())
// Intentionally not providing baseLibJar as lib file nor classpath file.
- .addClasspathFiles()
.addProgramFiles(
extLibJarMap.getForConfiguration(kotlinc, targetVersion),
appJarMap.getForConfiguration(kotlinc, targetVersion))
@@ -97,6 +96,9 @@
// Keep the main entry.
.addKeepMainRule(main)
.addKeepAttributes(ProguardKeepAttributes.RUNTIME_VISIBLE_ANNOTATIONS)
+ .addDontWarn(PKG + ".**")
+ .addDontWarnJetBrainsNotNullAnnotation()
+ .addDontWarnKotlin()
.allowDiagnosticWarningMessages()
// -dontoptimize so that basic code structure is kept.
.noOptimization()
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 8adeb48..3949d6e 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
@@ -91,6 +91,7 @@
.addKeepRules("-keep class **.UtilKt")
.addKeepRules("-keepclassmembers class * { ** comma*Join*(...); }")
.addKeepAttributes(ProguardKeepAttributes.RUNTIME_VISIBLE_ANNOTATIONS)
+ .addDontWarnJetBrainsNotNullAnnotation()
.compile()
.inspect(this::inspectMerged)
.writeToZip();
@@ -135,6 +136,7 @@
// Keep yet rename joinOf*(String).
.addKeepRules("-keepclassmembers,allowobfuscation class * { ** joinOf*(...); }")
.addKeepAttributes(ProguardKeepAttributes.RUNTIME_VISIBLE_ANNOTATIONS)
+ .addDontWarnJetBrainsNotNullAnnotation()
.compile()
.inspect(this::inspectRenamed)
.writeToZip();
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 a2a85c3..f84fa48 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
@@ -78,6 +78,7 @@
// Keep Itf, but allow minification.
.addKeepRules("-keep,allowobfuscation class **.Itf")
.addKeepAttributes(ProguardKeepAttributes.RUNTIME_VISIBLE_ANNOTATIONS)
+ .addDontWarnJetBrainsNotNullAnnotation()
.compile()
.inspect(this::inspect)
.writeToZip();
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 d012f10..af4c9b6 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
@@ -84,6 +84,7 @@
.addKeepRules("-keep class **.Person { <init>(...); }")
.addKeepRules("-keepclassmembers class **.Person { *** get*(); }")
.addKeepAttributes(ProguardKeepAttributes.RUNTIME_VISIBLE_ANNOTATIONS)
+ .addDontWarnJetBrainsNotNullAnnotation()
.compile()
.inspect(this::inspectGetterOnly)
.writeToZip();
@@ -179,6 +180,7 @@
// Keep LibKt extension methods
.addKeepRules("-keep class **.LibKt { <methods>; }")
.addKeepAttributes(ProguardKeepAttributes.RUNTIME_VISIBLE_ANNOTATIONS)
+ .addDontWarnJetBrainsNotNullAnnotation()
.compile()
.inspect(this::inspectSetterOnly)
.writeToZip();
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 9641f64..0113f19 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
@@ -76,6 +76,7 @@
// Keep non-private members of Impl
.addKeepRules("-keep public class **.Impl { !private *; }")
.addKeepAttributes(ProguardKeepAttributes.RUNTIME_VISIBLE_ANNOTATIONS)
+ .addDontWarnJetBrainsNotNullAnnotation()
.compile()
.inspect(this::inspect)
.writeToZip();
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 9783f3b..c8144f4 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
@@ -78,6 +78,7 @@
// Keep Itf, but allow minification.
.addKeepRules("-keep,allowobfuscation class **.Itf")
.addKeepAttributes(ProguardKeepAttributes.RUNTIME_VISIBLE_ANNOTATIONS)
+ .addDontWarnJetBrainsNotNullAnnotation()
.compile()
.inspect(this::inspect)
.writeToZip();
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 f33fc30..db927a8 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
@@ -77,6 +77,7 @@
.addKeepAttributes(ProguardKeepAttributes.RUNTIME_VISIBLE_ANNOTATIONS)
.addKeepAttributes(
ProguardKeepAttributes.INNER_CLASSES, ProguardKeepAttributes.ENCLOSING_METHOD)
+ .addDontWarnJetBrainsAnnotations()
.compile()
.writeToZip();
Path output =
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 2dbbc48..ef43f6e 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
@@ -87,6 +87,7 @@
// Keep the factory object and utils
.addKeepRules("-keep class **.ExprFactory { *; }")
.addKeepAttributes(ProguardKeepAttributes.RUNTIME_VISIBLE_ANNOTATIONS)
+ .addDontWarnJetBrainsAnnotations()
.compile()
.inspect(this::inspectValid)
.writeToZip();
@@ -152,6 +153,7 @@
// Keep the extension function
.addKeepRules("-keep class **.LibKt { <methods>; }")
.addKeepAttributes(ProguardKeepAttributes.RUNTIME_VISIBLE_ANNOTATIONS)
+ .addDontWarnJetBrainsNotNullAnnotation()
.compile()
.inspect(this::inspectInvalid)
.writeToZip();
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 edade6e..e680d6e 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
@@ -129,6 +129,7 @@
ProguardKeepAttributes.SIGNATURE,
ProguardKeepAttributes.INNER_CLASSES,
ProguardKeepAttributes.ENCLOSING_METHOD)
+ .addDontWarnJetBrainsNotNullAnnotation()
.compile()
.inspect(this::inspect)
.writeToZip();
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 788abf2..acdda5b 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
@@ -133,6 +133,7 @@
ProguardKeepAttributes.SIGNATURE,
ProguardKeepAttributes.INNER_CLASSES,
ProguardKeepAttributes.ENCLOSING_METHOD)
+ .addDontWarnJetBrainsNotNullAnnotation()
.compile()
.inspect(this::inspect)
.writeToZip();
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 133dd37..ac232d7 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
@@ -81,6 +81,7 @@
ProguardKeepAttributes.SIGNATURE,
ProguardKeepAttributes.INNER_CLASSES,
ProguardKeepAttributes.ENCLOSING_METHOD)
+ .addDontWarnJetBrainsNotNullAnnotation()
.compile()
.inspect(this::inspect)
.writeToZip();
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 83b8e62..0558ebc 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
@@ -89,6 +89,8 @@
.addProgramFiles(kotlincLibJar.getForConfiguration(kotlinc, targetVersion))
.addKeepAllClassesRule()
.addKeepAttributes(ProguardKeepAttributes.RUNTIME_VISIBLE_ANNOTATIONS)
+ .addDontWarnJetBrainsNotNullAnnotation()
+ .addDontWarnKotlin()
.compile()
.inspect(this::inspect)
.writeToZip();
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 40c7573..056b18b 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
@@ -60,6 +60,7 @@
.applyIf(keepMetadata, TestShrinkerBuilder::addKeepKotlinMetadata)
.addKeepRuntimeVisibleAnnotations()
.allowDiagnosticWarningMessages()
+ .addDontWarnJetBrainsAnnotations()
.compile()
.assertAllWarningMessagesMatch(equalTo("Resource 'META-INF/MANIFEST.MF' already exists."))
.inspect(inspector -> inspect(inspector, keepMetadata));
@@ -72,6 +73,7 @@
.addClasspathFiles(ToolHelper.getKotlinStdlibJar(kotlinc))
.addKeepRules("-keep class " + LIB_CLASS_NAME)
.addKeepRuntimeVisibleAnnotations()
+ .addDontWarnJetBrainsNotNullAnnotation()
.compile()
.inspect(inspector -> inspect(inspector, true));
}
@@ -84,6 +86,7 @@
.addLibraryFiles(ToolHelper.getJava8RuntimeJar())
.addKeepRules("-keep class " + LIB_CLASS_NAME)
.addKeepRuntimeVisibleAnnotations()
+ .addDontWarnJetBrainsNotNullAnnotation()
.compile()
.inspect(inspector -> inspect(inspector, true));
}
@@ -94,6 +97,8 @@
.addProgramFiles(libJars.getForConfiguration(kotlinc, targetVersion))
.addKeepRules("-keep class " + LIB_CLASS_NAME)
.addKeepRuntimeVisibleAnnotations()
+ .addDontWarnJetBrainsNotNullAnnotation()
+ .addDontWarnKotlin()
.compile()
.inspect(inspector -> inspect(inspector, true));
}
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 8c681bd..74159a6 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
@@ -47,6 +47,7 @@
.addKeepKotlinMetadata()
.addKeepRules("-keep class kotlin.io.** { *; }")
.addKeepAttributes(ProguardKeepAttributes.RUNTIME_VISIBLE_ANNOTATIONS)
+ .addDontWarnJetBrainsAnnotations()
.compile()
.inspect(this::inspect);
}
@@ -59,6 +60,7 @@
.addKeepRules("-keep class kotlin.io.** { *; }")
.addKeepRules("-if class *", "-keep class kotlin.Metadata { *; }")
.addKeepAttributes(ProguardKeepAttributes.RUNTIME_VISIBLE_ANNOTATIONS)
+ .addDontWarnJetBrainsAnnotations()
.compile()
.inspect(this::inspect);
}
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 3e62729..0b65ec3 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
@@ -44,6 +44,7 @@
.addKeepAllClassesRule()
.addKeepKotlinMetadata()
.addKeepAttributes(ProguardKeepAttributes.RUNTIME_VISIBLE_ANNOTATIONS)
+ .addDontWarnJetBrainsAnnotations()
.compile()
.inspect(
inspector ->
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 39793cf..3d3386c 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
@@ -75,6 +75,7 @@
.addProgramFiles(libJars.getForConfiguration(kotlinc, targetVersion))
.addKeepRules("-keep class " + PKG_LIB + ".Sub { <init>(); *** kept(); }")
.addKeepRuntimeVisibleAnnotations()
+ .addDontWarnKotlinMetadata()
.noMinification()
.compile()
.inspect(this::checkPruned)
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 168cbc5..1a2cd73 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
@@ -61,6 +61,7 @@
.addKeepMainRule(mainClassName)
.addKeepKotlinMetadata()
.addKeepAttributes(ProguardKeepAttributes.RUNTIME_VISIBLE_ANNOTATIONS)
+ .addDontWarnJetBrainsAnnotations()
.allowDiagnosticWarningMessages()
.setMinApi(parameters.getApiLevel())
.compile()
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 9cd2614..e23ac8f 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
@@ -56,6 +56,7 @@
.setMinApi(parameters.getApiLevel())
.addKeepAllClassesRule()
.addKeepAttributes(ProguardKeepAttributes.RUNTIME_VISIBLE_ANNOTATIONS)
+ .addDontWarnJetBrainsAnnotations()
.compile()
.inspect(inspector -> inspectMetadataVersion(inspector, "1.4.0"));
}
@@ -68,6 +69,7 @@
.setMinApi(parameters.getApiLevel())
.addKeepAllClassesRule()
.addKeepAttributes(ProguardKeepAttributes.RUNTIME_VISIBLE_ANNOTATIONS)
+ .addDontWarnJetBrainsAnnotations()
.compile()
.inspect(inspector -> inspectMetadataVersion(inspector, "1.4.0"));
}
@@ -80,6 +82,7 @@
.setMinApi(parameters.getApiLevel())
.addKeepAllClassesRule()
.addKeepAttributes(ProguardKeepAttributes.RUNTIME_VISIBLE_ANNOTATIONS)
+ .addDontWarnJetBrainsAnnotations()
.compile()
.inspect(inspector -> inspectMetadataVersion(inspector, "1.4.2"));
}
diff --git a/src/test/java/com/android/tools/r8/kotlin/optimize/switches/KotlinEnumSwitchTest.java b/src/test/java/com/android/tools/r8/kotlin/optimize/switches/KotlinEnumSwitchTest.java
index d7eeae4..fe05714 100644
--- a/src/test/java/com/android/tools/r8/kotlin/optimize/switches/KotlinEnumSwitchTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/optimize/switches/KotlinEnumSwitchTest.java
@@ -46,6 +46,7 @@
options.enableEnumValueOptimization = enableSwitchMapRemoval;
options.enableEnumSwitchMapRemoval = enableSwitchMapRemoval;
})
+ .addDontWarnJetBrainsNotNullAnnotation()
.setMinApi(parameters.getRuntime())
.noMinification()
.compile()
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 53b9d9b..ac7eb23 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
@@ -96,6 +96,7 @@
.addKeepAllClassesRule()
.addKeepAttributes(ProguardKeepAttributes.RUNTIME_VISIBLE_ANNOTATIONS)
.allowDiagnosticWarningMessages()
+ .addDontWarnJetBrains()
.compile()
.writeToZip(foo.toPath())
.assertAllWarningMessagesMatch(equalTo("Resource 'META-INF/MANIFEST.MF' already exists."))
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 6b70ca2..92b6b97 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
@@ -76,6 +76,7 @@
.allowAccessModification()
.allowDiagnosticWarningMessages(parameters.isCfRuntime())
.addKeepMainRule(MAIN)
+ .addDontWarnJetBrainsNotNullAnnotation()
.compileWithExpectedDiagnostics(
diagnostics ->
diagnostics.assertAllWarningsMatch(
diff --git a/src/test/java/com/android/tools/r8/maindexlist/MainDexDevirtualizerTest.java b/src/test/java/com/android/tools/r8/maindexlist/MainDexDevirtualizerTest.java
new file mode 100644
index 0000000..6cb7ab4
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/maindexlist/MainDexDevirtualizerTest.java
@@ -0,0 +1,178 @@
+// 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 com.android.tools.r8.utils.codeinspector.Matchers.isPresentAndRenamed;
+import static org.hamcrest.CoreMatchers.hasItem;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.core.IsNot.not;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assume.assumeTrue;
+
+import com.android.tools.r8.NeverClassInline;
+import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.NoVerticalClassMerging;
+import com.android.tools.r8.R8FullTestBuilder;
+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.utils.Box;
+import com.android.tools.r8.utils.StringUtils;
+import com.android.tools.r8.utils.codeinspector.CheckCastInstructionSubject;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import com.android.tools.r8.utils.codeinspector.InstructionSubject;
+import com.android.tools.r8.utils.codeinspector.MethodSubject;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.function.BiConsumer;
+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 MainDexDevirtualizerTest extends TestBase {
+
+ private final TestParameters parameters;
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
+ }
+
+ public MainDexDevirtualizerTest(TestParameters parameters) {
+ this.parameters = parameters;
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ assumeTrue(parameters.isCfRuntime() || !parameters.getDexRuntimeVersion().isDalvik());
+ runTest(
+ testBuilder -> {},
+ (inspector, mainDexClasses) -> {
+ assertTrue(mainDexClasses.isEmpty());
+ // Verify that the call to I.foo in Main.class has been changed to A.foo by checking for a
+ // cast.
+ ClassSubject clazz = inspector.clazz(Main.class);
+ assertThat(clazz, isPresentAndNotRenamed());
+ MethodSubject main = clazz.uniqueMethodWithName("main");
+ assertThat(main, isPresent());
+ List<CheckCastInstructionSubject> checkCasts =
+ main.streamInstructions()
+ .filter(InstructionSubject::isCheckCast)
+ .map(InstructionSubject::asCheckCast)
+ .collect(Collectors.toList());
+ assertEquals(1, checkCasts.size());
+ ClassSubject a = inspector.clazz(A.class);
+ assertThat(a, isPresentAndRenamed());
+ assertEquals(
+ a.getFinalDescriptor(), checkCasts.get(0).getType().getDescriptor().toString());
+ });
+ }
+
+ @Test
+ public void testMainDexClasses() throws Exception {
+ assumeTrue(parameters.isDexRuntime());
+ assumeTrue(parameters.getDexRuntimeVersion().isDalvik());
+ runTest(
+ r8FullTestBuilder ->
+ r8FullTestBuilder.addMainDexListClasses(I.class, Provider.class, Main.class),
+ this::inspect);
+ }
+
+ @Test
+ public void testMainDexTracing() throws Exception {
+ assumeTrue(parameters.isDexRuntime());
+ assumeTrue(parameters.getDexRuntimeVersion().isDalvik());
+ runTest(
+ r8FullTestBuilder -> r8FullTestBuilder.addMainDexClassRules(Main.class, I.class),
+ this::inspect);
+ }
+
+ private void runTest(
+ ThrowableConsumer<R8FullTestBuilder> setMainDexConsumer,
+ BiConsumer<CodeInspector, List<String>> resultConsumer)
+ throws Exception {
+ Box<String> mainDexStringList = new Box<>("");
+ testForR8(parameters.getBackend())
+ .addProgramClasses(I.class, Provider.class, A.class, Main.class)
+ .enableNoVerticalClassMergingAnnotations()
+ .enableInliningAnnotations()
+ .enableNeverClassInliningAnnotations()
+ .addKeepMainRule(Main.class)
+ .addKeepClassRulesWithAllowObfuscation(I.class)
+ .setMinApi(parameters.getApiLevel())
+ .applyIf(
+ parameters.isDexRuntime() && parameters.getDexRuntimeVersion().isDalvik(),
+ builder ->
+ builder.setMainDexListConsumer(ToolHelper.consumeString(mainDexStringList::set)))
+ .apply(setMainDexConsumer)
+ .run(parameters.getRuntime(), Main.class)
+ .apply(
+ result ->
+ resultConsumer.accept(
+ result.inspector(),
+ mainDexStringList.get().equals("")
+ ? new ArrayList<>()
+ : StringUtils.splitLines(mainDexStringList.get())));
+ }
+
+ private void inspect(CodeInspector inspector, List<String> mainDexClasses) {
+ assertEquals(4, inspector.allClasses().size());
+ assertEquals(3, mainDexClasses.size());
+ inspectClassInMainDex(Main.class, inspector, mainDexClasses);
+ inspectClassInMainDex(I.class, inspector, mainDexClasses);
+ inspectClassInMainDex(Provider.class, inspector, mainDexClasses);
+ ClassSubject aClass = inspector.clazz(A.class);
+ assertThat(aClass, isPresentAndRenamed());
+ assertThat(mainDexClasses, not(hasItem(aClass.getFinalBinaryName() + ".class")));
+ }
+
+ private void inspectClassInMainDex(
+ Class<?> clazz, CodeInspector inspector, List<String> mainDexClasses) {
+ ClassSubject classSubject = inspector.clazz(clazz);
+ assertThat(classSubject, isPresent());
+ assertThat(mainDexClasses, hasItem(classSubject.getFinalBinaryName() + ".class"));
+ }
+
+ @NoVerticalClassMerging
+ public interface I {
+
+ @NeverInline
+ void foo();
+ }
+
+ public static class Provider {
+ @NeverInline
+ public static I getImpl() {
+ return new A(); // <-- We will call-site optimize getImpl() to always return A.
+ }
+ }
+
+ @NeverClassInline
+ public static class A implements I {
+
+ @Override
+ @NeverInline
+ public void foo() {
+ System.out.println("A::foo");
+ }
+ }
+
+ public static class Main {
+
+ public static void main(String[] args) {
+ // The de-virtualizer will try and rebind from I.foo to A.foo.
+ Provider.getImpl().foo();
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/maindexlist/MainDexListFromGenerateMainDexInliningTest.java b/src/test/java/com/android/tools/r8/maindexlist/MainDexListFromGenerateMainDexInliningTest.java
new file mode 100644
index 0000000..94392e6
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/maindexlist/MainDexListFromGenerateMainDexInliningTest.java
@@ -0,0 +1,151 @@
+// 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.isAbsent;
+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.NoStaticClassMerging;
+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 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 MainDexListFromGenerateMainDexInliningTest 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(MainDexListFromGenerateMainDexInliningTest.class)
+ .addLibraryFiles(ToolHelper.getMostRecentAndroidJar())
+ .addMainDexRules(
+ "-keep class " + Main.class.getTypeName() + " {",
+ " public static void main(java.lang.String[]);",
+ "}")
+ .run()
+ .getMainDexList();
+ }
+
+ public MainDexListFromGenerateMainDexInliningTest(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)
+ .collectMainDexClasses()
+ .enableInliningAnnotations()
+ .enableNoHorizontalClassMergingAnnotations()
+ .enableNoStaticClassMergingAnnotations()
+ .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());
+
+ ClassSubject aClassSubject = inspector.clazz(A.class);
+ // TODO(b/178353726): Should be present, but was inlined.
+ assertThat(aClassSubject, isAbsent());
+
+ MethodSubject barMethodSubject = aClassSubject.uniqueMethodWithName("bar");
+ // TODO(b/178353726): Should be present, but was inlined.
+ assertThat(barMethodSubject, isAbsent());
+
+ ClassSubject bClassSubject = inspector.clazz(B.class);
+ assertThat(bClassSubject, isPresent());
+
+ MethodSubject bazMethodSubject = bClassSubject.uniqueMethodWithName("baz");
+ assertThat(bazMethodSubject, isPresent());
+
+ // TODO(b/178353726): foo() should invoke bar() and bar() should invoke baz().
+ assertThat(fooMethodSubject, 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());
+ });
+ }
+
+ static class Main {
+
+ public static void main(String[] args) {
+ System.out.println("Main.main()");
+ }
+
+ 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.
+ A.bar();
+ }
+ }
+
+ @NoHorizontalClassMerging
+ @NoStaticClassMerging
+ static class A {
+
+ static void bar() {
+ B.baz();
+ }
+ }
+
+ @NoHorizontalClassMerging
+ @NoStaticClassMerging
+ static class B {
+
+ @NeverInline
+ static void baz() {
+ System.out.println("B.baz");
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/maindexlist/MainDexListInliningTest.java b/src/test/java/com/android/tools/r8/maindexlist/MainDexListInliningTest.java
new file mode 100644
index 0000000..82f5e7d
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/maindexlist/MainDexListInliningTest.java
@@ -0,0 +1,92 @@
+// 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.isAbsent;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import com.android.tools.r8.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.utils.codeinspector.ClassSubject;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class MainDexListInliningTest extends TestBase {
+
+ private final TestParameters parameters;
+
+ @Parameterized.Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters()
+ .withDexRuntimes()
+ .withApiLevelsEndingAtExcluding(apiLevelWithNativeMultiDexSupport())
+ .build();
+ }
+
+ public MainDexListInliningTest(TestParameters parameters) {
+ this.parameters = parameters;
+ }
+
+ @Test
+ public void test() throws Exception {
+ R8TestCompileResult compileResult =
+ testForR8(parameters.getBackend())
+ .addInnerClasses(getClass())
+ .addKeepMainRule(Main.class)
+ .addMainDexListClasses(Main.class)
+ .collectMainDexClasses()
+ .enableNoHorizontalClassMergingAnnotations()
+ .setMinApi(parameters.getApiLevel())
+ .compile();
+
+ CodeInspector inspector = compileResult.inspector();
+
+ ClassSubject mainClassSubject = inspector.clazz(Main.class);
+ assertThat(mainClassSubject, isPresent());
+
+ // A is absent due to inlining.
+ // TODO(b/178353726): Inlining should be prohibited.
+ ClassSubject aClassSubject = inspector.clazz(A.class);
+ assertThat(aClassSubject, isAbsent());
+
+ // B should be referenced from Main.main.
+ ClassSubject bClassSubject = inspector.clazz(B.class);
+ assertThat(bClassSubject, isPresent());
+
+ compileResult.inspectMainDexClasses(
+ mainDexClasses -> {
+ assertTrue(mainDexClasses.contains(mainClassSubject.getFinalName()));
+ assertFalse(mainDexClasses.contains(bClassSubject.getFinalName()));
+ });
+ }
+
+ static class Main {
+
+ public static void main(String[] args) {
+ // Should be inlined.
+ A.m();
+ }
+ }
+
+ @NoHorizontalClassMerging
+ static class A {
+
+ public static void m() {
+ System.out.println(B.class);
+ }
+ }
+
+ @NoHorizontalClassMerging
+ static class B {}
+}
diff --git a/src/test/java/com/android/tools/r8/maindexlist/MainDexListMergeInRootTest.java b/src/test/java/com/android/tools/r8/maindexlist/MainDexListMergeInRootTest.java
new file mode 100644
index 0000000..7041c75
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/maindexlist/MainDexListMergeInRootTest.java
@@ -0,0 +1,113 @@
+// 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.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 org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class MainDexListMergeInRootTest extends TestBase {
+
+ private final TestParameters parameters;
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withDexRuntimes().withAllApiLevels().build();
+ }
+
+ public MainDexListMergeInRootTest(TestParameters parameters) {
+ this.parameters = parameters;
+ }
+
+ @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")));
+ }));
+ }
+
+ @NoHorizontalClassMerging
+ @NeverClassInline
+ public static class OutsideMainDex {
+
+ @NeverInline
+ public void print(int i) {
+ System.out.println("OutsideMainDex::print" + i);
+ }
+ }
+
+ @NeverClassInline
+ public static class InsideA {
+
+ public void bar() {
+ System.out.println("A::live");
+ }
+
+ /* Not a traced root */
+ @NeverInline
+ public void foo(int i) {
+ new OutsideMainDex().print(i);
+ }
+ }
+
+ @NeverClassInline
+ public static class InsideB {
+
+ @NeverInline
+ public void foo(int i) {
+ System.out.println("InsideB::live" + i);
+ }
+ }
+
+ public static class Main {
+
+ public static void main(String[] args) {
+ new InsideB().foo(args.length);
+ new InsideA().bar();
+ }
+
+ public void keptToKeepInsideANotLive() {
+ new InsideA().foo(System.currentTimeMillis() > 0 ? 0 : 1);
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/maindexlist/MainDexListNoDirectDependenciesTest.java b/src/test/java/com/android/tools/r8/maindexlist/MainDexListNoDirectDependenciesTest.java
new file mode 100644
index 0000000..8f5d997
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/maindexlist/MainDexListNoDirectDependenciesTest.java
@@ -0,0 +1,86 @@
+// 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.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+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.utils.codeinspector.ClassSubject;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class MainDexListNoDirectDependenciesTest extends TestBase {
+
+ private final TestParameters parameters;
+
+ @Parameterized.Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters()
+ .withDexRuntimes()
+ .withApiLevelsEndingAtExcluding(apiLevelWithNativeMultiDexSupport())
+ .build();
+ }
+
+ public MainDexListNoDirectDependenciesTest(TestParameters parameters) {
+ this.parameters = parameters;
+ }
+
+ @Test
+ public void test() throws Exception {
+ R8TestCompileResult compileResult =
+ testForR8(parameters.getBackend())
+ .addInnerClasses(getClass())
+ .addMainDexListClasses(A.class)
+ .addMainDexClassRules(B.class)
+ .collectMainDexClasses()
+ .noTreeShaking()
+ .setMinApi(parameters.getApiLevel())
+ .compile();
+
+ CodeInspector inspector = compileResult.inspector();
+
+ ClassSubject aClassSubject = inspector.clazz(A.class);
+ ClassSubject referencedFromAClassSubject = inspector.clazz(ReferencedFromA.class);
+ ClassSubject bClassSubject = inspector.clazz(B.class);
+ ClassSubject referencedFromBClassSubject = inspector.clazz(ReferencedFromB.class);
+
+ compileResult.inspectMainDexClasses(
+ mainDexClasses -> {
+ assertTrue(mainDexClasses.contains(aClassSubject.getFinalName()));
+ // It is assumed that the provided main dex list includes its direct dependencies.
+ // Therefore, we explicitly do not include the direct dependencies of the main dex list
+ // classes in the final main dex, since this would lead to the dependencies of the
+ // dependencies being included in the main dex.
+ assertFalse(mainDexClasses.contains(referencedFromAClassSubject.getFinalName()));
+ assertTrue(mainDexClasses.contains(bClassSubject.getFinalName()));
+ assertTrue(mainDexClasses.contains(referencedFromBClassSubject.getFinalName()));
+ });
+ }
+
+ static class A {
+
+ public void m() {
+ System.out.println(ReferencedFromA.class);
+ }
+ }
+
+ static class ReferencedFromA {}
+
+ static class B {
+
+ public void m() {
+ System.out.println(ReferencedFromB.class);
+ }
+ }
+
+ static class ReferencedFromB {}
+}
diff --git a/src/test/java/com/android/tools/r8/maindexlist/MainDexListOutputTest.java b/src/test/java/com/android/tools/r8/maindexlist/MainDexListOutputTest.java
index 7565cf7..1bbcb9e 100644
--- a/src/test/java/com/android/tools/r8/maindexlist/MainDexListOutputTest.java
+++ b/src/test/java/com/android/tools/r8/maindexlist/MainDexListOutputTest.java
@@ -6,6 +6,7 @@
import static com.android.tools.r8.DiagnosticsMatcher.diagnosticMessage;
import static org.hamcrest.CoreMatchers.containsString;
+import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
@@ -16,6 +17,7 @@
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.synthesis.SyntheticItemsTestUtils;
import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.FileUtils;
import com.google.common.collect.ImmutableList;
@@ -59,8 +61,8 @@
@Override
public void finished(DiagnosticsHandler handler) {
String string = builder.toString();
- assertTrue(string.contains(testClassMainDexName));
- assertTrue(string.contains("Lambda"));
+ assertThat(string, containsString(testClassMainDexName));
+ assertThat(string, SyntheticItemsTestUtils.containsExternalSyntheticReference());
}
}
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 52b60fe..ccaa66a 100644
--- a/src/test/java/com/android/tools/r8/maindexlist/MainDexTracingTest.java
+++ b/src/test/java/com/android/tools/r8/maindexlist/MainDexTracingTest.java
@@ -22,11 +22,12 @@
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.ir.desugar.LambdaRewriter;
import com.android.tools.r8.references.Reference;
import com.android.tools.r8.shaking.WhyAreYouKeepingConsumer;
+import com.android.tools.r8.synthesis.SyntheticItemsTestUtils;
import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.Box;
import com.android.tools.r8.utils.DescriptorUtils;
@@ -210,7 +211,12 @@
Paths.get(EXAMPLE_SRC_DIR, "multidex", "main-dex-rules.txt"),
Paths.get(EXAMPLE_O_SRC_DIR, "multidex004", "ref-list-1.txt"),
Paths.get(EXAMPLE_O_SRC_DIR, "multidex004", "ref-list-1.txt"),
- AndroidApiLevel.I);
+ AndroidApiLevel.I,
+ builder ->
+ builder
+ .applyIf(
+ backend.isDex(), TestShrinkerBuilder::addDontWarnCompilerSynthesizedAnnotations)
+ .addOptionsModification(options -> options.enableInlining = false));
}
@Test
@@ -433,13 +439,13 @@
for (int i = 0; i < refList.length; i++) {
String reference = refList[i].trim();
// The main dex list generator does not do any lambda desugaring.
- if (!isLambda(reference)) {
+ if (!isExternalSyntheticLambda(reference)) {
if (mainDexGeneratorMainDexList.size() <= i - nonLambdaOffset) {
fail("Main dex list generator is missing '" + reference + "'");
}
String fromList = mainDexGeneratorMainDexList.get(i - nonLambdaOffset);
String fromConsumer = mainDexGeneratorMainDexListFromConsumer.get(i - nonLambdaOffset);
- if (isLambda(fromList)) {
+ if (isExternalSyntheticLambda(fromList)) {
assertEquals(Backend.DEX, backend);
assertEquals(fromList, fromConsumer);
nonLambdaOffset--;
@@ -481,8 +487,8 @@
assertArrayEquals(entriesUnsorted, entriesSorted);
}
- private boolean isLambda(String mainDexEntry) {
- return mainDexEntry.contains(LambdaRewriter.LAMBDA_CLASS_NAME_PREFIX);
+ private boolean isExternalSyntheticLambda(String mainDexEntry) {
+ return SyntheticItemsTestUtils.isExternalLambda(Reference.classFromDescriptor(mainDexEntry));
}
private String mainDexStringToDescriptor(String mainDexString) {
@@ -492,11 +498,8 @@
}
private void checkSameMainDexEntry(String reference, String computed) {
- if (isLambda(reference)) {
- // For lambda classes we check that there is a lambda class for the right containing
- // class. However, we do not check the hash for the generated lambda class. The hash
- // changes for different compiler versions because different compiler versions generate
- // different lambda implementation method names.
+ if (isExternalSyntheticLambda(reference)) {
+ // For synthetic classes we check that the context classes match.
reference = reference.substring(0, reference.lastIndexOf('$'));
computed = computed.substring(0, computed.lastIndexOf('$'));
}
diff --git a/src/test/java/com/android/tools/r8/maindexlist/MainDexWithSynthesizedClassesTest.java b/src/test/java/com/android/tools/r8/maindexlist/MainDexWithSynthesizedClassesTest.java
index d102004..9fa7fe9 100644
--- a/src/test/java/com/android/tools/r8/maindexlist/MainDexWithSynthesizedClassesTest.java
+++ b/src/test/java/com/android/tools/r8/maindexlist/MainDexWithSynthesizedClassesTest.java
@@ -11,7 +11,7 @@
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
-import com.android.tools.r8.ir.desugar.LambdaRewriter;
+import com.android.tools.r8.synthesis.SyntheticItemsTestUtils;
import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.StringUtils;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
@@ -100,10 +100,11 @@
inspector.allClasses().stream()
.anyMatch(
clazz ->
- clazz.getOriginalName().contains(LambdaRewriter.LAMBDA_CLASS_NAME_PREFIX)
+ clazz.isSynthesizedJavaLambdaClass()
&& clazz
- .getOriginalName()
- .contains("$" + lambdaHolder.getSimpleName() + "$")));
+ .getOriginalReference()
+ .equals(
+ SyntheticItemsTestUtils.syntheticLambdaClass(lambdaHolder, 0))));
}
static class TestClass {
diff --git a/src/test/java/com/android/tools/r8/maindexlist/warnings/MainDexWarningsTest.java b/src/test/java/com/android/tools/r8/maindexlist/warnings/MainDexWarningsTest.java
index 4f5e54a..22bad85 100644
--- a/src/test/java/com/android/tools/r8/maindexlist/warnings/MainDexWarningsTest.java
+++ b/src/test/java/com/android/tools/r8/maindexlist/warnings/MainDexWarningsTest.java
@@ -57,6 +57,7 @@
.addKeepMainRule(mainClass)
// Include main dex rule for class Static.
.addMainDexClassRules(Main.class, Static.class)
+ .enableForceInliningAnnotations()
.setMinApi(parameters.getApiLevel())
.compile()
.inspect(this::classStaticGone)
@@ -69,9 +70,11 @@
.setMinApi(AndroidApiLevel.K)
.addProgramClasses(testClassesWithoutStatic)
.addKeepMainRule(mainClass)
+ .addDontWarn(Static.class)
// Include explicit main dex entry for class Static.
.addMainDexListClasses(Static.class)
.allowDiagnosticWarningMessages()
+ .enableForceInliningAnnotations()
.setMinApi(parameters.getApiLevel())
.compile()
.inspect(this::classStaticGone)
@@ -91,7 +94,9 @@
.addMainDexListClasses(Main.class, Static.class)
// Include main dex rule for class Static2.
.addMainDexClassRules(Static2.class)
+ .addDontWarn(Static.class)
.allowDiagnosticWarningMessages()
+ .enableForceInliningAnnotations()
.setMinApi(parameters.getApiLevel())
.compile()
.inspect(this::classStaticGone)
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 837459c..8dd68fc 100644
--- a/src/test/java/com/android/tools/r8/naming/EnumMinificationKotlinTest.java
+++ b/src/test/java/com/android/tools/r8/naming/EnumMinificationKotlinTest.java
@@ -64,6 +64,7 @@
.addProgramFiles(getJavaJarFile(FOLDER))
.addKeepMainRule(MAIN_CLASS_NAME)
.addKeepClassRulesWithAllowObfuscation(ENUM_CLASS_NAME)
+ .addDontWarnJetBrainsAnnotations()
.allowDiagnosticWarningMessages()
.minification(minify)
.setMinApi(parameters.getApiLevel())
diff --git a/src/test/java/com/android/tools/r8/naming/FieldNamingObfuscationDictionaryTest.java b/src/test/java/com/android/tools/r8/naming/FieldNamingObfuscationDictionaryTest.java
index 0ab866f..165d0f7 100644
--- a/src/test/java/com/android/tools/r8/naming/FieldNamingObfuscationDictionaryTest.java
+++ b/src/test/java/com/android/tools/r8/naming/FieldNamingObfuscationDictionaryTest.java
@@ -99,6 +99,7 @@
.addInnerClasses(FieldNamingObfuscationDictionaryTest.class)
.addKeepRules("-overloadaggressively", "-obfuscationdictionary " + dictionary.toString())
.addKeepMainRule(Runner.class)
+ .enableInliningAnnotations()
.enableNeverClassInliningAnnotations()
.enableNoHorizontalClassMergingAnnotations()
.setMinApi(parameters.getApiLevel())
diff --git a/src/test/java/com/android/tools/r8/naming/InterfaceConstructorRenamingTest.java b/src/test/java/com/android/tools/r8/naming/InterfaceConstructorRenamingTest.java
index ebd7420..7ec2589 100644
--- a/src/test/java/com/android/tools/r8/naming/InterfaceConstructorRenamingTest.java
+++ b/src/test/java/com/android/tools/r8/naming/InterfaceConstructorRenamingTest.java
@@ -6,7 +6,6 @@
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
-import com.android.tools.r8.ArchiveClassFileProvider;
import com.android.tools.r8.R8TestCompileResult;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
@@ -16,10 +15,8 @@
import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.StringUtils;
import com.android.tools.r8.utils.codeinspector.MethodSubject;
-import com.google.common.collect.ImmutableSet;
import java.io.IOException;
import java.util.Collections;
-import java.util.concurrent.ExecutionException;
import java.util.function.Function;
import org.junit.ClassRule;
import org.junit.Test;
@@ -44,17 +41,11 @@
@ClassRule public static TemporaryFolder staticTemp = ToolHelper.getTemporaryFolderForTest();
private static R8TestCompileResult compile(Backend backend)
- throws com.android.tools.r8.CompilationFailedException, IOException, ExecutionException {
+ throws com.android.tools.r8.CompilationFailedException, IOException {
R8TestCompileResult compileResult =
testForR8(staticTemp, backend)
.addProgramClasses(TestInterface.class, TestClass.class)
.addKeepMainRule(TestClass.class)
- .addLibraryProvider(
- new ArchiveClassFileProvider(
- ToolHelper.getJava8RuntimeJar(),
- name ->
- ImmutableSet.of("java/lang/Object", "java/lang/System")
- .contains(name.replace(".class", ""))))
.setMinApi(AndroidApiLevel.B)
.compile()
.assertNoMessages();
diff --git a/src/test/java/com/android/tools/r8/naming/IntersectionLambdaTest.java b/src/test/java/com/android/tools/r8/naming/IntersectionLambdaTest.java
index efb5d35..5b8dc11 100644
--- a/src/test/java/com/android/tools/r8/naming/IntersectionLambdaTest.java
+++ b/src/test/java/com/android/tools/r8/naming/IntersectionLambdaTest.java
@@ -51,7 +51,6 @@
.assertSuccessWithOutputLines(EXPECTED);
}
- @FunctionalInterface
@NoVerticalClassMerging
public interface I {
void foo();
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 70a9cb2..3d143a1 100644
--- a/src/test/java/com/android/tools/r8/naming/KotlinIntrinsicsIdentifierTest.java
+++ b/src/test/java/com/android/tools/r8/naming/KotlinIntrinsicsIdentifierTest.java
@@ -88,6 +88,7 @@
.addProgramFiles(compiledJars.getForConfiguration(kotlinc, targetVersion))
.addProgramFiles(getJavaJarFile(FOLDER))
.addKeepMainRule(mainClassName)
+ .addDontWarnJetBrainsNotNullAnnotation()
.allowDiagnosticWarningMessages()
.minification(minification)
.compile()
@@ -149,6 +150,7 @@
"-" + NoHorizontalClassMergingRule.RULE_NAME + " class **." + targetClassName,
"-" + NoStaticClassMergingRule.RULE_NAME + " class **." + targetClassName,
"-neverinline class **." + targetClassName + " { <methods>; }"))
+ .addDontWarnJetBrainsNotNullAnnotation()
.allowDiagnosticWarningMessages()
.minification(minification)
.compile()
diff --git a/src/test/java/com/android/tools/r8/naming/applymapping/ApplyMappingAfterVerticalMergingMethodTest.java b/src/test/java/com/android/tools/r8/naming/applymapping/ApplyMappingAfterVerticalMergingMethodTest.java
index 4d000bb..3cb240b 100644
--- a/src/test/java/com/android/tools/r8/naming/applymapping/ApplyMappingAfterVerticalMergingMethodTest.java
+++ b/src/test/java/com/android/tools/r8/naming/applymapping/ApplyMappingAfterVerticalMergingMethodTest.java
@@ -123,7 +123,8 @@
List<FoundMethodSubject> methods =
inspector.clazz(LibrarySubclass.class).allMethods();
assertEquals(3, methods.size());
- assertEquals(1, methods.stream().filter(m -> m.isInstanceInitializer()).count());
+ assertEquals(
+ 1, methods.stream().filter(FoundMethodSubject::isInstanceInitializer).count());
assertEquals(
1, methods.stream().filter(m -> m.getFinalName().contains("main")).count());
assertEquals(
diff --git a/src/test/java/com/android/tools/r8/naming/applymapping/ApplyMappingDesugarLambdaTest.java b/src/test/java/com/android/tools/r8/naming/applymapping/ApplyMappingDesugarLambdaTest.java
index 0d0af87..9db718e 100644
--- a/src/test/java/com/android/tools/r8/naming/applymapping/ApplyMappingDesugarLambdaTest.java
+++ b/src/test/java/com/android/tools/r8/naming/applymapping/ApplyMappingDesugarLambdaTest.java
@@ -104,7 +104,6 @@
}
}
- @FunctionalInterface
public interface I {
void doStuff();
diff --git a/src/test/java/com/android/tools/r8/naming/applymapping/ApplyMappingInnerClassesPreserveTest.java b/src/test/java/com/android/tools/r8/naming/applymapping/ApplyMappingInnerClassesPreserveTest.java
index 6d61f51..2f7c01c 100644
--- a/src/test/java/com/android/tools/r8/naming/applymapping/ApplyMappingInnerClassesPreserveTest.java
+++ b/src/test/java/com/android/tools/r8/naming/applymapping/ApplyMappingInnerClassesPreserveTest.java
@@ -26,7 +26,7 @@
@Parameters(name = "{0}")
public static TestParametersCollection data() {
- return getTestParameters().withAllRuntimes().build();
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
}
public ApplyMappingInnerClassesPreserveTest(TestParameters parameters) {
@@ -41,7 +41,7 @@
.addProgramClassesAndInnerClasses(InnerLibraryClass.class)
.addKeepAllClassesRuleWithAllowObfuscation()
.addKeepClassAndMembersRulesWithAllowObfuscation(InnerLibraryClass.class)
- .setMinApi(parameters.getRuntime())
+ .setMinApi(parameters.getApiLevel())
.compile();
testForR8(parameters.getBackend())
.addProgramClassesAndInnerClasses(ProgramClassWithSimpleLibraryReference.class)
@@ -49,7 +49,7 @@
.addKeepMainRule(ProgramClassWithSimpleLibraryReference.class)
.addApplyMapping(libraryCompileResult.getProguardMap())
.addKeepAttributes("EnclosingMethod", "InnerClasses")
- .setMinApi(parameters.getRuntime())
+ .setMinApi(parameters.getApiLevel())
.noTreeShaking()
.compile()
.addRunClasspathFiles(libraryCompileResult.writeToZip())
diff --git a/src/test/java/com/android/tools/r8/naming/applymapping/ApplyMappingInterfaceClInitTest.java b/src/test/java/com/android/tools/r8/naming/applymapping/ApplyMappingInterfaceClInitTest.java
index 6887f59..de95714 100644
--- a/src/test/java/com/android/tools/r8/naming/applymapping/ApplyMappingInterfaceClInitTest.java
+++ b/src/test/java/com/android/tools/r8/naming/applymapping/ApplyMappingInterfaceClInitTest.java
@@ -86,7 +86,6 @@
void foo();
}
- @NeverClassInline
public static class Main implements TestInterface {
public static void main(String[] args) {
@@ -94,7 +93,6 @@
}
@Override
- @NeverInline
public void foo() {
System.out.println("Hello World!");
}
diff --git a/src/test/java/com/android/tools/r8/naming/applymapping/ApplyMappingMultipleInterfacesTest.java b/src/test/java/com/android/tools/r8/naming/applymapping/ApplyMappingMultipleInterfacesTest.java
index b56464e..af6c004 100644
--- a/src/test/java/com/android/tools/r8/naming/applymapping/ApplyMappingMultipleInterfacesTest.java
+++ b/src/test/java/com/android/tools/r8/naming/applymapping/ApplyMappingMultipleInterfacesTest.java
@@ -5,7 +5,6 @@
package com.android.tools.r8.naming.applymapping;
import com.android.tools.r8.CompilationFailedException;
-import com.android.tools.r8.NeverInline;
import com.android.tools.r8.R8TestCompileResult;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
@@ -46,7 +45,6 @@
public static class ImplementsI2I3 implements I2, I3 {
@Override
- @NeverInline
public String foo(String bar) {
System.out.print("Hello" + bar);
return bar;
@@ -55,7 +53,6 @@
public static class ImplementsI3 implements I3 {
@Override
- @NeverInline
public String foo(String bar) {
System.out.print("Goodbye");
return bar;
@@ -84,7 +81,7 @@
@Parameters(name = "{0}")
public static TestParametersCollection data() {
- return getTestParameters().withAllRuntimes().build();
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
}
public ApplyMappingMultipleInterfacesTest(TestParameters parameters) {
@@ -98,19 +95,24 @@
testForR8(parameters.getBackend())
.addProgramClasses(I2Minified.class, I3Minified.class)
.addKeepAllClassesRule()
- .setMinApi(parameters.getRuntime())
+ .setMinApi(parameters.getApiLevel())
.compile();
testForR8(parameters.getBackend())
.addClasspathClasses(I2.class, I3.class)
.addProgramClasses(MainForImplements.class, ImplementsI2I3.class, ImplementsI3.class)
.addKeepMainRule(MainForImplements.class)
.addApplyMapping(
- I2.class.getTypeName() + " -> " + I2Minified.class.getTypeName() + ":\n" +
- " java.lang.String foo(java.lang.String) -> a\n" +
- I3.class.getTypeName() + " -> " + I3Minified.class.getTypeName() + ":\n" +
- " java.lang.String foo(java.lang.String) -> a"
- )
- .setMinApi(parameters.getRuntime())
+ I2.class.getTypeName()
+ + " -> "
+ + I2Minified.class.getTypeName()
+ + ":\n"
+ + " java.lang.String foo(java.lang.String) -> a\n"
+ + I3.class.getTypeName()
+ + " -> "
+ + I3Minified.class.getTypeName()
+ + ":\n"
+ + " java.lang.String foo(java.lang.String) -> a")
+ .setMinApi(parameters.getApiLevel())
.compile()
.addRunClasspathFiles(libraryResult.writeToZip())
.run(parameters.getRuntime(), MainForImplements.class)
diff --git a/src/test/java/com/android/tools/r8/naming/applymapping/shared/InnerLibraryClass.java b/src/test/java/com/android/tools/r8/naming/applymapping/shared/InnerLibraryClass.java
index 1e751d1..8cfa59b 100644
--- a/src/test/java/com/android/tools/r8/naming/applymapping/shared/InnerLibraryClass.java
+++ b/src/test/java/com/android/tools/r8/naming/applymapping/shared/InnerLibraryClass.java
@@ -4,12 +4,9 @@
package com.android.tools.r8.naming.applymapping.shared;
-import com.android.tools.r8.NeverInline;
-
public class InnerLibraryClass {
public static class LibraryClass {
- @NeverInline
public void foo() {
System.out.println("LibraryClass.foo()");
}
diff --git a/src/test/java/com/android/tools/r8/naming/applymapping/shared/ProgramClassWithSimpleLibraryReference.java b/src/test/java/com/android/tools/r8/naming/applymapping/shared/ProgramClassWithSimpleLibraryReference.java
index 42b7907..a42654f 100644
--- a/src/test/java/com/android/tools/r8/naming/applymapping/shared/ProgramClassWithSimpleLibraryReference.java
+++ b/src/test/java/com/android/tools/r8/naming/applymapping/shared/ProgramClassWithSimpleLibraryReference.java
@@ -12,7 +12,6 @@
public static class SubLibraryClass extends LibraryClass {
@Override
- @NeverInline
public void foo() {
System.out.println("SubLibraryClass.foo()");
super.foo();
diff --git a/src/test/java/com/android/tools/r8/naming/b126592786/B126592786.java b/src/test/java/com/android/tools/r8/naming/b126592786/B126592786.java
index de81c15..b2614a3 100644
--- a/src/test/java/com/android/tools/r8/naming/b126592786/B126592786.java
+++ b/src/test/java/com/android/tools/r8/naming/b126592786/B126592786.java
@@ -44,7 +44,7 @@
Class<?> mainClass = genericTypeLive ? MainGenericTypeLive.class : MainGenericTypeNotLive.class;
testForR8(backend)
.minification(minify)
- .addProgramClasses(GetClassUtil.class, A.class, GenericType.class, mainClass)
+ .addProgramClasses(GetClassUtil.class, A.class, GenericType.class, mainClass, Marker.class)
.addKeepMainRule(mainClass)
.addKeepRules(
"-keep class " + GetClassUtil.class.getTypeName() + " {",
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 ef791d3..69f0148 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
@@ -64,6 +64,7 @@
options.testing.validInliningReasons = ImmutableSet.of(Reason.FORCE);
options.enableClassInlining = false;
})
+ .addDontWarnJetBrainsAnnotations()
.setMinApi(parameters.getApiLevel())
.run(parameters.getRuntime(), Main.class)
.assertSuccessWithOutput(StringUtils.lines("11", "12"))
diff --git a/src/test/java/com/android/tools/r8/naming/methodparameters/MethodParametersTest.java b/src/test/java/com/android/tools/r8/naming/methodparameters/MethodParametersTest.java
index 30e72d7..a8e337e 100644
--- a/src/test/java/com/android/tools/r8/naming/methodparameters/MethodParametersTest.java
+++ b/src/test/java/com/android/tools/r8/naming/methodparameters/MethodParametersTest.java
@@ -14,6 +14,7 @@
import com.android.tools.r8.R8TestRunResult;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.ToolHelper;
import com.android.tools.r8.ToolHelper.DexVm.Version;
import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.BooleanUtils;
@@ -58,7 +59,10 @@
.addKeepClassAndMembersRulesWithAllowObfuscation(MethodParametersTest.class)
.addKeepMainRule(MethodParametersTest.class)
.addKeepRules(keepMethodParameters ? "-keepattributes MethodParameters" : "")
- .setMinApi(AndroidApiLevel.L)
+ .setMinApi(keepMethodParameters ? AndroidApiLevel.O : AndroidApiLevel.L)
+ // java.lang.reflect.Parameter was introduced in API level 26 (O).
+ .addLibraryFiles(ToolHelper.getAndroidJar(AndroidApiLevel.O))
+ .compile()
.run(parameters.getRuntime(), MethodParametersTest.class);
if (keepMethodParameters) {
checkOutputContainsAll(runResult.getStdOut());
diff --git a/src/test/java/com/android/tools/r8/naming/retrace/DesugarLambdaRetraceTest.java b/src/test/java/com/android/tools/r8/naming/retrace/DesugarLambdaRetraceTest.java
index 3959d84..2836f61 100644
--- a/src/test/java/com/android/tools/r8/naming/retrace/DesugarLambdaRetraceTest.java
+++ b/src/test/java/com/android/tools/r8/naming/retrace/DesugarLambdaRetraceTest.java
@@ -14,6 +14,8 @@
import com.android.tools.r8.CompilationMode;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.naming.retrace.StackTrace.StackTraceLine;
+import com.android.tools.r8.references.Reference;
+import com.android.tools.r8.synthesis.SyntheticItemsTestUtils;
import com.android.tools.r8.utils.BooleanUtils;
import com.google.common.collect.ImmutableList;
import java.util.Collection;
@@ -57,7 +59,8 @@
}
private boolean isSynthesizedLambdaFrame(StackTraceLine line) {
- return line.className.contains("-$$Lambda$");
+ // TODO(141287349): The mapping should not map the external name to the internal name!
+ return SyntheticItemsTestUtils.isInternalLambda(Reference.classFromTypeName(line.className));
}
private void checkLambdaFrame(StackTrace retracedStackTrace) {
@@ -141,7 +144,6 @@
}
// Custom consumer functional interface, as java.util.function.Consumer is not present on Android.
-@FunctionalInterface
interface ConsumerDesugarLambdaRetraceTest<T> {
void accept(T value);
}
diff --git a/src/test/java/com/android/tools/r8/naming/retrace/InliningRetraceTest.java b/src/test/java/com/android/tools/r8/naming/retrace/InliningRetraceTest.java
index 2ffe206..344a716 100644
--- a/src/test/java/com/android/tools/r8/naming/retrace/InliningRetraceTest.java
+++ b/src/test/java/com/android/tools/r8/naming/retrace/InliningRetraceTest.java
@@ -84,7 +84,9 @@
@Override
public void configure(R8TestBuilder<?> builder) {
- builder.applyIf(mode == CompilationMode.RELEASE, R8TestBuilder::enableForceInliningAnnotations);
+ builder
+ .addForceInliningAnnotations()
+ .applyIf(mode == CompilationMode.RELEASE, R8TestBuilder::enableForceInliningAnnotations);
}
@Override
diff --git a/src/test/java/com/android/tools/r8/naming/retraceproguard/DesugarLambdaRetraceTest.java b/src/test/java/com/android/tools/r8/naming/retraceproguard/DesugarLambdaRetraceTest.java
index a527d93..458cb07 100644
--- a/src/test/java/com/android/tools/r8/naming/retraceproguard/DesugarLambdaRetraceTest.java
+++ b/src/test/java/com/android/tools/r8/naming/retraceproguard/DesugarLambdaRetraceTest.java
@@ -166,7 +166,6 @@
}
// Custom consumer functional interface, as java.util.function.Consumer is not present on Android.
-@FunctionalInterface
interface ConsumerDesugarLambdaRetraceTest<T> {
void accept(T value);
}
diff --git a/src/test/java/com/android/tools/r8/naming/retraceproguard/DesugarStaticInterfaceMethodsRetraceTest.java b/src/test/java/com/android/tools/r8/naming/retraceproguard/DesugarStaticInterfaceMethodsRetraceTest.java
index 2034f5d..47240a5 100644
--- a/src/test/java/com/android/tools/r8/naming/retraceproguard/DesugarStaticInterfaceMethodsRetraceTest.java
+++ b/src/test/java/com/android/tools/r8/naming/retraceproguard/DesugarStaticInterfaceMethodsRetraceTest.java
@@ -9,6 +9,7 @@
import com.android.tools.r8.CompilationMode;
import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.R8TestBuilder;
import com.android.tools.r8.ToolHelper;
import com.android.tools.r8.utils.BooleanUtils;
import com.google.common.collect.ImmutableList;
@@ -33,6 +34,11 @@
}
@Override
+ public void configure(R8TestBuilder builder) {
+ builder.enableInliningAnnotations();
+ }
+
+ @Override
public Collection<Class<?>> getClasses() {
return ImmutableList.of(
getMainClass(), InterfaceWithStaticMethod1.class, InterfaceWithStaticMethod2.class);
diff --git a/src/test/java/com/android/tools/r8/naming/retraceproguard/RetraceTestBase.java b/src/test/java/com/android/tools/r8/naming/retraceproguard/RetraceTestBase.java
index e94483f..62c4de9 100644
--- a/src/test/java/com/android/tools/r8/naming/retraceproguard/RetraceTestBase.java
+++ b/src/test/java/com/android/tools/r8/naming/retraceproguard/RetraceTestBase.java
@@ -48,12 +48,12 @@
public void runTest(List<String> keepRules, BiConsumer<StackTrace, StackTrace> checker)
throws Exception {
-
R8TestRunResult result =
(compat ? testForR8Compat(backend) : testForR8(backend))
.setMode(mode)
.enableProguardTestOptions()
.addProgramClasses(getClasses())
+ .addForceInliningAnnotations()
.addKeepMainRule(getMainClass())
.addKeepRules(keepRules)
.apply(this::configure)
diff --git a/src/test/java/com/android/tools/r8/regress/b63935662/Regress63935662.java b/src/test/java/com/android/tools/r8/regress/b63935662/Regress63935662.java
index 1151d20..ced781f 100644
--- a/src/test/java/com/android/tools/r8/regress/b63935662/Regress63935662.java
+++ b/src/test/java/com/android/tools/r8/regress/b63935662/Regress63935662.java
@@ -8,6 +8,7 @@
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.utils.AndroidApiLevel;
import com.google.common.collect.ImmutableList;
import java.util.List;
import org.junit.Test;
@@ -21,7 +22,10 @@
@Parameterized.Parameters(name = "{0}")
public static TestParametersCollection data() {
- return getTestParameters().withAllRuntimesAndApiLevels().build();
+ return getTestParameters()
+ .withAllRuntimes()
+ .withApiLevelsStartingAtIncluding(AndroidApiLevel.N)
+ .build();
}
public Regress63935662(TestParameters parameters) {
diff --git a/src/test/java/com/android/tools/r8/repackage/RepackageWithInitClassTest.java b/src/test/java/com/android/tools/r8/repackage/RepackageWithInitClassTest.java
index e548bcf..c8a47ca 100644
--- a/src/test/java/com/android/tools/r8/repackage/RepackageWithInitClassTest.java
+++ b/src/test/java/com/android/tools/r8/repackage/RepackageWithInitClassTest.java
@@ -49,6 +49,7 @@
.addInnerClasses(getClass())
.addClassObfuscationDictionary("a")
.addKeepMainRule(TestClass.class)
+ .addMemberValuePropagationAnnotations()
.apply(this::configureRepackaging)
.enableMemberValuePropagationAnnotations(enableMemberValuePropagationAnnotations)
.setMinApi(parameters.getApiLevel())
diff --git a/src/test/java/com/android/tools/r8/repackage/RepackageWithPackagePrivateFieldTypeTest.java b/src/test/java/com/android/tools/r8/repackage/RepackageWithPackagePrivateFieldTypeTest.java
index 9dd90f3..543dbf8 100644
--- a/src/test/java/com/android/tools/r8/repackage/RepackageWithPackagePrivateFieldTypeTest.java
+++ b/src/test/java/com/android/tools/r8/repackage/RepackageWithPackagePrivateFieldTypeTest.java
@@ -63,7 +63,6 @@
public static class IneligibleForRepackaging {
- @NeverPropagateValue
private static NonPublicKeptClass FIELD =
System.currentTimeMillis() > 0 ? new PublicSubClass() : null;
diff --git a/src/test/java/com/android/tools/r8/repackage/testclasses/repackagetest/AccessPackagePrivateMethodOnKeptClassIndirect.java b/src/test/java/com/android/tools/r8/repackage/testclasses/repackagetest/AccessPackagePrivateMethodOnKeptClassIndirect.java
index 2319ca4..7190c3f 100644
--- a/src/test/java/com/android/tools/r8/repackage/testclasses/repackagetest/AccessPackagePrivateMethodOnKeptClassIndirect.java
+++ b/src/test/java/com/android/tools/r8/repackage/testclasses/repackagetest/AccessPackagePrivateMethodOnKeptClassIndirect.java
@@ -7,7 +7,6 @@
import com.android.tools.r8.NeverInline;
import com.android.tools.r8.NoHorizontalClassMerging;
import com.android.tools.r8.NoStaticClassMerging;
-import com.android.tools.r8.NoVerticalClassMerging;
@NoHorizontalClassMerging
@NoStaticClassMerging
@@ -18,7 +17,8 @@
Helper.test();
}
- @NoVerticalClassMerging
+ @NoHorizontalClassMerging
+ @NoStaticClassMerging
public static class Helper {
@NeverInline
diff --git a/src/test/java/com/android/tools/r8/resolution/InvokeDefaultMethodViaStaticTest.java b/src/test/java/com/android/tools/r8/resolution/InvokeDefaultMethodViaStaticTest.java
index 2080b42..37f328e 100644
--- a/src/test/java/com/android/tools/r8/resolution/InvokeDefaultMethodViaStaticTest.java
+++ b/src/test/java/com/android/tools/r8/resolution/InvokeDefaultMethodViaStaticTest.java
@@ -6,7 +6,6 @@
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
-import com.android.tools.r8.TestRunResult;
import com.android.tools.r8.resolution.invokestaticinterfacedefault.InterfaceDump;
import com.android.tools.r8.resolution.invokestaticinterfacedefault.MainDump;
import com.google.common.collect.ImmutableList;
@@ -35,34 +34,20 @@
@Test
public void testReference() throws Exception {
- TestRunResult<?> result =
- testForRuntime(parameters)
- .addProgramClassFileData(CLASSES)
- .run(parameters.getRuntime(), "Main");
- if (parameters.isDexRuntime()
- && parameters.getApiLevel().isLessThan(apiLevelWithDefaultInterfaceMethodsSupport())) {
- // TODO(b/167535447): Desugaring should preserve the error.
- result.assertFailureWithErrorThatThrows(NoSuchMethodError.class);
- } else {
- result.assertFailureWithErrorThatThrows(IncompatibleClassChangeError.class);
- }
+ testForRuntime(parameters)
+ .addProgramClassFileData(CLASSES)
+ .run(parameters.getRuntime(), "Main")
+ .assertFailureWithErrorThatThrows(IncompatibleClassChangeError.class);
}
@Test
public void testR8() throws Exception {
- TestRunResult<?> result =
- testForR8(parameters.getBackend())
- .addProgramClassFileData(CLASSES)
- .addKeepMainRule("Main")
- .setMinApi(parameters.getApiLevel())
- .addOptionsModification(o -> o.testing.allowInvokeErrors = true)
- .run(parameters.getRuntime(), "Main");
- if (parameters.isDexRuntime()
- && parameters.getApiLevel().isLessThan(apiLevelWithDefaultInterfaceMethodsSupport())) {
- // TODO(b/167535447): Desugaring should preserve the error.
- result.assertFailureWithErrorThatThrows(NoClassDefFoundError.class);
- } else {
- result.assertFailureWithErrorThatThrows(IncompatibleClassChangeError.class);
- }
+ testForR8(parameters.getBackend())
+ .addProgramClassFileData(CLASSES)
+ .addKeepMainRule("Main")
+ .setMinApi(parameters.getApiLevel())
+ .addOptionsModification(o -> o.testing.allowInvokeErrors = true)
+ .run(parameters.getRuntime(), "Main")
+ .assertFailureWithErrorThatThrows(IncompatibleClassChangeError.class);
}
}
diff --git a/src/test/java/com/android/tools/r8/resolution/SingleStaticTargetLookupTestRunner.java b/src/test/java/com/android/tools/r8/resolution/SingleStaticTargetLookupTestRunner.java
index fef40af..8edefd8 100644
--- a/src/test/java/com/android/tools/r8/resolution/SingleStaticTargetLookupTestRunner.java
+++ b/src/test/java/com/android/tools/r8/resolution/SingleStaticTargetLookupTestRunner.java
@@ -17,6 +17,7 @@
import com.android.tools.r8.TestShrinkerBuilder;
import com.android.tools.r8.utils.StringUtils;
import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.android.tools.r8.utils.codeinspector.InstructionSubject;
import com.android.tools.r8.utils.codeinspector.MethodSubject;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -65,7 +66,8 @@
assertThat(clazz, isPresent());
assertThat(main, isPresent());
assertEquals(keepFoo, staticFoo.isPresent());
- assertEquals(keepFoo, main.streamInstructions().anyMatch(i -> i.isInvokeStatic()));
+ assertEquals(
+ keepFoo, main.streamInstructions().anyMatch(InstructionSubject::isInvokeStatic));
});
}
diff --git a/src/test/java/com/android/tools/r8/resolution/VirtualOverrideOfPrivateStaticMethodWithVirtualParentTest.java b/src/test/java/com/android/tools/r8/resolution/VirtualOverrideOfPrivateStaticMethodWithVirtualParentTest.java
index 5da5b2d..1d6dc16 100644
--- a/src/test/java/com/android/tools/r8/resolution/VirtualOverrideOfPrivateStaticMethodWithVirtualParentTest.java
+++ b/src/test/java/com/android/tools/r8/resolution/VirtualOverrideOfPrivateStaticMethodWithVirtualParentTest.java
@@ -44,6 +44,7 @@
public static class B extends A implements I {}
public static class C extends B {
+ @Override
public void f() {
System.out.println("Called C.f");
}
diff --git a/src/test/java/com/android/tools/r8/resolution/access/NestInvokeSpecialInterfaceMethodAccessTest.java b/src/test/java/com/android/tools/r8/resolution/access/NestInvokeSpecialInterfaceMethodAccessTest.java
index 3f7568a..425f3b0 100644
--- a/src/test/java/com/android/tools/r8/resolution/access/NestInvokeSpecialInterfaceMethodAccessTest.java
+++ b/src/test/java/com/android/tools/r8/resolution/access/NestInvokeSpecialInterfaceMethodAccessTest.java
@@ -11,14 +11,15 @@
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestRunResult;
+import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexClassAndMethod;
+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.ResolutionResult;
import com.android.tools.r8.graph.ResolutionResult.NoSuchMethodResult;
import com.android.tools.r8.references.Reference;
-import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.android.tools.r8.transformers.ClassFileTransformer;
import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.BooleanUtils;
@@ -108,15 +109,15 @@
Class<?> declaredClass = symbolicReferenceIsDefiningType ? definingClass : A.class;
Class<?> callerClass = A.class;
- AppView<AppInfoWithLiveness> appView = getAppView();
- AppInfoWithLiveness appInfo = appView.appInfo();
+ AppView<AppInfoWithClassHierarchy> appView = getAppView();
+ AppInfoWithClassHierarchy appInfo = appView.appInfo();
- DexProgramClass definingClassDefinition = getDexProgramClass(definingClass, appInfo);
- DexProgramClass declaredClassDefinition = getDexProgramClass(declaredClass, appInfo);
- DexProgramClass callerClassDefinition = getDexProgramClass(callerClass, appInfo);
+ DexProgramClass definingClassDefinition = getDexProgramClass(definingClass, appView);
+ DexProgramClass declaredClassDefinition = getDexProgramClass(declaredClass, appView);
+ DexProgramClass callerClassDefinition = getDexProgramClass(callerClass, appView);
- DexMethod method = getTargetMethodSignature(declaredClass, appInfo);
- assertCallingClassCallsTarget(callerClass, appInfo, method);
+ DexMethod method = getTargetMethodSignature(declaredClass, appView.dexItemFactory());
+ assertCallingClassCallsTarget(callerClass, appView, method);
// Resolve the method from the point of the declared holder.
assertEquals(method.holder, declaredClassDefinition.type);
@@ -136,7 +137,7 @@
DexClassAndMethod targetSuper =
resolutionResult.lookupInvokeSuperTarget(callerClassDefinition, appInfo);
if (inSameNest) {
- assertEquals(definingClassDefinition.type, targetSpecial.getHolderType());
+ assertEquals(definingClassDefinition.getType(), targetSpecial.getHolderType());
assertEquals(targetSpecial.getReference(), targetSuper.getReference());
} else {
assertNull(targetSpecial);
@@ -145,30 +146,30 @@
}
private void assertCallingClassCallsTarget(
- Class<?> callerClass, AppInfoWithLiveness appInfo, DexMethod target) {
- CodeInspector inspector = new CodeInspector(appInfo.app());
+ Class<?> callerClass, AppView<?> appView, DexMethod target) {
+ CodeInspector inspector = new CodeInspector(appView.appInfo().app());
MethodSubject foo = inspector.clazz(callerClass).uniqueMethodWithName("foo");
assertTrue(
foo.streamInstructions().anyMatch(i -> i.isInvokeSpecial() && i.getMethod() == target));
}
- private DexMethod getTargetMethodSignature(Class<?> declaredClass, AppInfoWithLiveness appInfo) {
+ private DexMethod getTargetMethodSignature(
+ Class<?> declaredClass, DexItemFactory dexItemFactory) {
return buildMethod(
Reference.method(Reference.classFromClass(declaredClass), "bar", ImmutableList.of(), null),
- appInfo.dexItemFactory());
+ dexItemFactory);
}
- private DexProgramClass getDexProgramClass(Class<?> clazz, AppInfoWithLiveness appInfo) {
- return appInfo.definitionFor(buildType(clazz, appInfo.dexItemFactory())).asProgramClass();
+ private DexProgramClass getDexProgramClass(Class<?> clazz, AppView<?> appView) {
+ return appView.definitionFor(buildType(clazz, appView.dexItemFactory())).asProgramClass();
}
- private AppView<AppInfoWithLiveness> getAppView() throws Exception {
- return computeAppViewWithLiveness(
+ private AppView<AppInfoWithClassHierarchy> getAppView() throws Exception {
+ return computeAppViewWithClassHierachy(
buildClasses(getClasses())
.addClassProgramData(getTransformedClasses())
.addLibraryFile(TestBase.runtimeJar(parameters.getBackend()))
- .build(),
- Main.class);
+ .build());
}
@Test
diff --git a/src/test/java/com/android/tools/r8/resolution/access/NestInvokeSpecialInterfaceMethodAccessWithIntermediateTest.java b/src/test/java/com/android/tools/r8/resolution/access/NestInvokeSpecialInterfaceMethodAccessWithIntermediateTest.java
index 364014c..07487ec 100644
--- a/src/test/java/com/android/tools/r8/resolution/access/NestInvokeSpecialInterfaceMethodAccessWithIntermediateTest.java
+++ b/src/test/java/com/android/tools/r8/resolution/access/NestInvokeSpecialInterfaceMethodAccessWithIntermediateTest.java
@@ -171,6 +171,10 @@
.addProgramClassFileData(getTransformedClasses())
.setMinApi(parameters.getApiLevel())
.addKeepMainRule(Main.class)
+ .applyIf(
+ !parameters.canUseDefaultAndStaticInterfaceMethods()
+ && !symbolicReferenceIsDefiningType,
+ builder -> builder.addDontWarnCompanionClass(J.class))
.run(parameters.getRuntime(), Main.class)
.apply(result -> checkExpectedResult(result, true));
}
diff --git a/src/test/java/com/android/tools/r8/resolution/access/NestInvokeSpecialMethodAccessTest.java b/src/test/java/com/android/tools/r8/resolution/access/NestInvokeSpecialMethodAccessTest.java
index 5a3d01e..c6a2dbf 100644
--- a/src/test/java/com/android/tools/r8/resolution/access/NestInvokeSpecialMethodAccessTest.java
+++ b/src/test/java/com/android/tools/r8/resolution/access/NestInvokeSpecialMethodAccessTest.java
@@ -11,6 +11,7 @@
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestRunResult;
+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.DexClassAndMethod;
@@ -18,7 +19,6 @@
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.ResolutionResult;
import com.android.tools.r8.references.Reference;
-import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.android.tools.r8.transformers.ClassFileTransformer;
import com.android.tools.r8.utils.BooleanUtils;
import com.android.tools.r8.utils.OptionalBool;
@@ -82,16 +82,16 @@
Class<?> declaredClass = A.class;
Class<?> callerClass = B.class;
- AppView<AppInfoWithLiveness> appView = getAppView();
- AppInfoWithLiveness appInfo = appView.appInfo();
+ AppView<AppInfoWithClassHierarchy> appView = getAppView();
+ AppInfoWithClassHierarchy appInfo = appView.appInfo();
- DexClass definingClassDefinition = getDexProgramClass(definingClass, appInfo);
- DexClass declaredClassDefinition = getDexProgramClass(declaredClass, appInfo);
- DexProgramClass callerClassDefinition = getDexProgramClass(callerClass, appInfo);
+ DexClass definingClassDefinition = getDexProgramClass(definingClass, appView);
+ DexClass declaredClassDefinition = getDexProgramClass(declaredClass, appView);
+ DexProgramClass callerClassDefinition = getDexProgramClass(callerClass, appView);
- DexMethod method = getTargetMethodSignature(declaredClass, appInfo);
+ DexMethod method = getTargetMethodSignature(declaredClass, appView);
- assertCallingClassCallsTarget(callerClass, appInfo, method);
+ assertCallingClassCallsTarget(callerClass, appView, method);
// Resolve the method from the point of the declared holder.
assertEquals(method.holder, declaredClassDefinition.type);
@@ -121,29 +121,28 @@
}
private void assertCallingClassCallsTarget(
- Class<?> callerClass, AppInfoWithLiveness appInfo, DexMethod target) {
- CodeInspector inspector = new CodeInspector(appInfo.app());
+ Class<?> callerClass, AppView<?> appView, DexMethod target) {
+ CodeInspector inspector = new CodeInspector(appView.appInfo().app());
MethodSubject foo = inspector.clazz(callerClass).uniqueMethodWithName("foo");
assertTrue(
foo.streamInstructions().anyMatch(i -> i.isInvokeSpecial() && i.getMethod() == target));
}
- private DexMethod getTargetMethodSignature(Class<?> declaredClass, AppInfoWithLiveness appInfo) {
+ private DexMethod getTargetMethodSignature(Class<?> declaredClass, AppView<?> appView) {
return buildMethod(
Reference.method(Reference.classFromClass(declaredClass), "bar", ImmutableList.of(), null),
- appInfo.dexItemFactory());
+ appView.dexItemFactory());
}
- private DexProgramClass getDexProgramClass(Class<?> definingClass, AppInfoWithLiveness appInfo) {
- return appInfo
- .definitionFor(buildType(definingClass, appInfo.dexItemFactory()))
+ private DexProgramClass getDexProgramClass(Class<?> definingClass, AppView<?> appView) {
+ return appView
+ .definitionFor(buildType(definingClass, appView.dexItemFactory()))
.asProgramClass();
}
- private AppView<AppInfoWithLiveness> getAppView() throws Exception {
- return computeAppViewWithLiveness(
- buildClasses(getClasses()).addClassProgramData(getTransformedClasses()).build(),
- Main.class);
+ private AppView<AppInfoWithClassHierarchy> getAppView() throws Exception {
+ return computeAppViewWithClassHierachy(
+ buildClasses(getClasses()).addClassProgramData(getTransformedClasses()).build());
}
@Test
diff --git a/src/test/java/com/android/tools/r8/resolution/access/NestInvokeSpecialMethodAccessWithIntermediateTest.java b/src/test/java/com/android/tools/r8/resolution/access/NestInvokeSpecialMethodAccessWithIntermediateTest.java
index b291f91..ce6f4d7 100644
--- a/src/test/java/com/android/tools/r8/resolution/access/NestInvokeSpecialMethodAccessWithIntermediateTest.java
+++ b/src/test/java/com/android/tools/r8/resolution/access/NestInvokeSpecialMethodAccessWithIntermediateTest.java
@@ -11,6 +11,7 @@
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestRunResult;
+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.DexClassAndMethod;
@@ -19,7 +20,6 @@
import com.android.tools.r8.graph.ResolutionResult;
import com.android.tools.r8.graph.ResolutionResult.IllegalAccessOrNoSuchMethodResult;
import com.android.tools.r8.references.Reference;
-import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.android.tools.r8.transformers.ClassFileTransformer;
import com.android.tools.r8.utils.BooleanUtils;
import com.android.tools.r8.utils.DescriptorUtils;
@@ -107,16 +107,16 @@
Class<?> declaredClass = symbolicReferenceIsDefiningType ? definingClass : B.class;
Class<?> callerClass = C.class;
- AppView<AppInfoWithLiveness> appView = getAppView();
- AppInfoWithLiveness appInfo = appView.appInfo();
+ AppView<AppInfoWithClassHierarchy> appView = getAppView();
+ AppInfoWithClassHierarchy appInfo = appView.appInfo();
- DexClass definingClassDefinition = getDexProgramClass(definingClass, appInfo);
- DexClass declaredClassDefinition = getDexProgramClass(declaredClass, appInfo);
- DexProgramClass callerClassDefinition = getDexProgramClass(callerClass, appInfo);
+ DexClass definingClassDefinition = getDexProgramClass(definingClass, appView);
+ DexClass declaredClassDefinition = getDexProgramClass(declaredClass, appView);
+ DexProgramClass callerClassDefinition = getDexProgramClass(callerClass, appView);
- DexMethod method = getTargetMethodSignature(declaredClass, appInfo);
+ DexMethod method = getTargetMethodSignature(declaredClass, appView);
- assertCallingClassCallsTarget(callerClass, appInfo, method);
+ assertCallingClassCallsTarget(callerClass, appView, method);
// Resolve the method from the point of the declared holder.
assertEquals(method.holder, declaredClassDefinition.type);
@@ -153,27 +153,26 @@
}
private void assertCallingClassCallsTarget(
- Class<?> callerClass, AppInfoWithLiveness appInfo, DexMethod target) {
- CodeInspector inspector = new CodeInspector(appInfo.app());
+ Class<?> callerClass, AppView<?> appView, DexMethod target) {
+ CodeInspector inspector = new CodeInspector(appView.appInfo().app());
MethodSubject foo = inspector.clazz(callerClass).uniqueMethodWithName("foo");
assertTrue(
foo.streamInstructions().anyMatch(i -> i.isInvokeSpecial() && i.getMethod() == target));
}
- private DexMethod getTargetMethodSignature(Class<?> declaredClass, AppInfoWithLiveness appInfo) {
+ private DexMethod getTargetMethodSignature(Class<?> declaredClass, AppView<?> appView) {
return buildMethod(
Reference.method(Reference.classFromClass(declaredClass), "bar", ImmutableList.of(), null),
- appInfo.dexItemFactory());
+ appView.dexItemFactory());
}
- private DexProgramClass getDexProgramClass(Class<?> clazz, AppInfoWithLiveness appInfo) {
- return appInfo.definitionFor(buildType(clazz, appInfo.dexItemFactory())).asProgramClass();
+ private DexProgramClass getDexProgramClass(Class<?> clazz, AppView<?> appView) {
+ return appView.definitionFor(buildType(clazz, appView.dexItemFactory())).asProgramClass();
}
- private AppView<AppInfoWithLiveness> getAppView() throws Exception {
- return computeAppViewWithLiveness(
- buildClasses(getClasses()).addClassProgramData(getTransformedClasses()).build(),
- Main.class);
+ private AppView<AppInfoWithClassHierarchy> getAppView() throws Exception {
+ return computeAppViewWithClassHierachy(
+ buildClasses(getClasses()).addClassProgramData(getTransformedClasses()).build());
}
@Test
diff --git a/src/test/java/com/android/tools/r8/resolution/interfacetargets/DefaultMethodAsOverrideWithLambdaTest.java b/src/test/java/com/android/tools/r8/resolution/interfacetargets/DefaultMethodAsOverrideWithLambdaTest.java
index 3215f17..8f800f6 100644
--- a/src/test/java/com/android/tools/r8/resolution/interfacetargets/DefaultMethodAsOverrideWithLambdaTest.java
+++ b/src/test/java/com/android/tools/r8/resolution/interfacetargets/DefaultMethodAsOverrideWithLambdaTest.java
@@ -93,7 +93,6 @@
.assertSuccessWithOutputLines(EXPECTED);
}
- @FunctionalInterface
@NoVerticalClassMerging
public interface I {
void foo();
diff --git a/src/test/java/com/android/tools/r8/resolution/interfacetargets/DefaultMethodLambdaTest.java b/src/test/java/com/android/tools/r8/resolution/interfacetargets/DefaultMethodLambdaTest.java
index 77fa9d8..169c255 100644
--- a/src/test/java/com/android/tools/r8/resolution/interfacetargets/DefaultMethodLambdaTest.java
+++ b/src/test/java/com/android/tools/r8/resolution/interfacetargets/DefaultMethodLambdaTest.java
@@ -89,7 +89,6 @@
.assertSuccessWithOutputLines(EXPECTED);
}
- @FunctionalInterface
@NoVerticalClassMerging
public interface I {
void foo();
diff --git a/src/test/java/com/android/tools/r8/resolution/interfacetargets/DefaultWithoutTopTest.java b/src/test/java/com/android/tools/r8/resolution/interfacetargets/DefaultWithoutTopTest.java
index 70a350f..16c1401 100644
--- a/src/test/java/com/android/tools/r8/resolution/interfacetargets/DefaultWithoutTopTest.java
+++ b/src/test/java/com/android/tools/r8/resolution/interfacetargets/DefaultWithoutTopTest.java
@@ -86,6 +86,9 @@
.addProgramClasses(I.class, J.class, Main.class)
.addProgramClassFileData(setAImplementsIAndJ())
.addKeepMainRule(Main.class)
+ .enableInliningAnnotations()
+ .enableNeverClassInliningAnnotations()
+ .enableNoVerticalClassMergingAnnotations()
.setMinApi(parameters.getApiLevel())
.run(parameters.getRuntime(), Main.class)
.assertSuccessWithOutputLines(EXPECTED);
@@ -131,6 +134,9 @@
.addProgramClasses(I.class, J.class, K.class, Main.class)
.addProgramClassFileData(setAimplementsIandK())
.addKeepMainRule(Main.class)
+ .enableInliningAnnotations()
+ .enableNeverClassInliningAnnotations()
+ .enableNoVerticalClassMergingAnnotations()
.setMinApi(parameters.getApiLevel())
.run(parameters.getRuntime(), Main.class)
.assertSuccessWithOutputLines(EXPECTED);
diff --git a/src/test/java/com/android/tools/r8/resolution/interfacetargets/LambdaMultipleInterfacesTest.java b/src/test/java/com/android/tools/r8/resolution/interfacetargets/LambdaMultipleInterfacesTest.java
index 5422f9b..958e785 100644
--- a/src/test/java/com/android/tools/r8/resolution/interfacetargets/LambdaMultipleInterfacesTest.java
+++ b/src/test/java/com/android/tools/r8/resolution/interfacetargets/LambdaMultipleInterfacesTest.java
@@ -90,7 +90,6 @@
.assertSuccessWithOutputLines(EXPECTED);
}
- @FunctionalInterface
@NoVerticalClassMerging
public interface I {
void foo();
diff --git a/src/test/java/com/android/tools/r8/resolution/virtualtargets/DefaultWithoutTopTest.java b/src/test/java/com/android/tools/r8/resolution/virtualtargets/DefaultWithoutTopTest.java
index 0daaa44..1e23ae3 100644
--- a/src/test/java/com/android/tools/r8/resolution/virtualtargets/DefaultWithoutTopTest.java
+++ b/src/test/java/com/android/tools/r8/resolution/virtualtargets/DefaultWithoutTopTest.java
@@ -86,6 +86,9 @@
.addProgramClasses(I.class, J.class, Main.class)
.addProgramClassFileData(setAImplementsIAndJ())
.addKeepMainRule(Main.class)
+ .enableInliningAnnotations()
+ .enableNeverClassInliningAnnotations()
+ .enableNoVerticalClassMergingAnnotations()
.setMinApi(parameters.getApiLevel())
.run(parameters.getRuntime(), Main.class)
.assertSuccessWithOutputLines(EXPECTED);
diff --git a/src/test/java/com/android/tools/r8/resolution/virtualtargets/KeptTargetsIncompleteLookupTest.java b/src/test/java/com/android/tools/r8/resolution/virtualtargets/KeptTargetsIncompleteLookupTest.java
index 3566d3a..bc35d2b 100644
--- a/src/test/java/com/android/tools/r8/resolution/virtualtargets/KeptTargetsIncompleteLookupTest.java
+++ b/src/test/java/com/android/tools/r8/resolution/virtualtargets/KeptTargetsIncompleteLookupTest.java
@@ -232,7 +232,7 @@
// ----- Program -----
// B extends A { } <-- initial
AppView<AppInfoWithClassHierarchy> appView =
- computeAppViewWithSubtyping(
+ computeAppViewWithClassHierachy(
buildClasses(Collections.singletonList(B.class), Arrays.asList(A.class, I.class))
.build());
AppInfoWithClassHierarchy appInfo = appView.appInfo();
diff --git a/src/test/java/com/android/tools/r8/resolution/virtualtargets/PackagePrivateFinalOverrideTest.java b/src/test/java/com/android/tools/r8/resolution/virtualtargets/PackagePrivateFinalOverrideTest.java
index e5904db..00f9b0b 100644
--- a/src/test/java/com/android/tools/r8/resolution/virtualtargets/PackagePrivateFinalOverrideTest.java
+++ b/src/test/java/com/android/tools/r8/resolution/virtualtargets/PackagePrivateFinalOverrideTest.java
@@ -11,8 +11,6 @@
import static org.junit.Assume.assumeTrue;
import com.android.tools.r8.CompilationFailedException;
-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;
@@ -101,6 +99,7 @@
testForR8(parameters.getBackend())
.addProgramClasses(MyViewModel.class, Main.class, ViewModel.class, ViewModelRunner.class)
.addKeepMainRule(Main.class)
+ .enableInliningAnnotations()
.setMinApi(parameters.getApiLevel())
.run(parameters.getRuntime(), Main.class)
.assertSuccessWithOutputLines(EXPECTED);
@@ -146,6 +145,7 @@
.addProgramClasses(MyViewModel.class, ViewModel.class, ViewModelRunner.class)
.addProgramClassFileData(getModifiedMainWithIllegalInvokeToViewModelClear())
.addKeepMainRule(Main.class)
+ .enableInliningAnnotations()
.setMinApi(parameters.getApiLevel())
.run(parameters.getRuntime(), Main.class)
.assertFailureWithErrorThatThrows(IllegalAccessError.class);
@@ -198,6 +198,7 @@
.addProgramClasses(MyViewModel.class, ViewModel.class, Main.class)
.addProgramClassFileData(getModifiedViewModelRunnerWithDirectMyViewModelTarget())
.addKeepMainRule(Main.class)
+ .enableInliningAnnotations()
.setMinApi(parameters.getApiLevel())
.run(parameters.getRuntime(), Main.class)
.assertSuccessWithOutputLines(AMBIGUOUS_EXPECTED_OUTPUT);
@@ -238,10 +239,8 @@
.transform();
}
- @NeverClassInline
public static class MyViewModel extends ViewModel {
- @NeverInline
public void clear() {
System.out.println("MyViewModel.clear()");
}
diff --git a/src/test/java/com/android/tools/r8/retrace/InlineWithoutNullCheckTest.java b/src/test/java/com/android/tools/r8/retrace/InlineWithoutNullCheckTest.java
index 3cabc0e..6973f6b 100644
--- a/src/test/java/com/android/tools/r8/retrace/InlineWithoutNullCheckTest.java
+++ b/src/test/java/com/android/tools/r8/retrace/InlineWithoutNullCheckTest.java
@@ -12,7 +12,6 @@
import com.android.tools.r8.AlwaysInline;
import com.android.tools.r8.NeverInline;
-import com.android.tools.r8.NeverPropagateValue;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
@@ -110,6 +109,7 @@
testForR8(parameters.getBackend())
.addInnerClasses(InlineWithoutNullCheckTest.class)
.addKeepMainRule(TestClassForInlineMethod.class)
+ .enableAlwaysInliningAnnotations()
.enableInliningAnnotations()
.addKeepAttributes(ProguardKeepAttributes.SOURCE_FILE)
.setMinApi(parameters.getApiLevel())
@@ -137,6 +137,7 @@
testForR8(parameters.getBackend())
.addInnerClasses(InlineWithoutNullCheckTest.class)
.addKeepMainRule(TestClassForInlineField.class)
+ .enableAlwaysInliningAnnotations()
.enableInliningAnnotations()
.addKeepAttributes(ProguardKeepAttributes.SOURCE_FILE)
.setMinApi(parameters.getApiLevel())
@@ -165,6 +166,7 @@
testForR8(parameters.getBackend())
.addInnerClasses(InlineWithoutNullCheckTest.class)
.addKeepMainRule(TestClassForInlineStaticField.class)
+ .enableAlwaysInliningAnnotations()
.enableInliningAnnotations()
.addKeepAttributes(ProguardKeepAttributes.SOURCE_FILE)
.setMinApi(parameters.getApiLevel())
@@ -232,7 +234,7 @@
}
static class A {
- @NeverPropagateValue Result result;
+ Result result;
A(Result result) {
this.result = result;
@@ -260,8 +262,8 @@
}
static class Result {
- @NeverPropagateValue int x = 1;
- @NeverPropagateValue static int y = 1;
+ int x = 1;
+ static int y = 1;
@AlwaysInline
int methodWhichAccessInstanceMethod() {
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 274b990..94e1e1a 100644
--- a/src/test/java/com/android/tools/r8/retrace/KotlinInlineFunctionInSameFileRetraceTests.java
+++ b/src/test/java/com/android/tools/r8/retrace/KotlinInlineFunctionInSameFileRetraceTests.java
@@ -91,6 +91,7 @@
testForR8(parameters.getBackend())
.addProgramFiles(compilationResults.getForConfiguration(kotlinc, targetVersion))
.addProgramFiles(ToolHelper.getKotlinStdlibJar(kotlinc))
+ .addDontWarnJetBrainsNotNullAnnotation()
.addKeepAttributes("SourceFile", "LineNumberTable")
.setMode(CompilationMode.RELEASE)
.addKeepMainRule(MAIN)
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 d22a979..3827c26 100644
--- a/src/test/java/com/android/tools/r8/retrace/KotlinInlineFunctionRetraceTest.java
+++ b/src/test/java/com/android/tools/r8/retrace/KotlinInlineFunctionRetraceTest.java
@@ -109,6 +109,7 @@
.allowDiagnosticWarningMessages()
.setMode(CompilationMode.RELEASE)
.addKeepMainRule(main)
+ .addDontWarnJetBrainsNotNullAnnotation()
.setMinApi(parameters.getApiLevel())
.compile()
.assertAllWarningMessagesMatch(equalTo("Resource 'META-INF/MANIFEST.MF' already exists."))
@@ -139,6 +140,7 @@
.allowDiagnosticWarningMessages()
.setMode(CompilationMode.RELEASE)
.addKeepMainRule(main)
+ .addDontWarnJetBrainsNotNullAnnotation()
.setMinApi(parameters.getApiLevel())
.compile()
.assertAllWarningMessagesMatch(equalTo("Resource 'META-INF/MANIFEST.MF' already exists."))
@@ -172,6 +174,7 @@
.allowDiagnosticWarningMessages()
.setMode(CompilationMode.RELEASE)
.addKeepMainRule(main)
+ .addDontWarnJetBrainsNotNullAnnotation()
.setMinApi(parameters.getApiLevel())
.compile()
.assertAllWarningMessagesMatch(equalTo("Resource 'META-INF/MANIFEST.MF' already exists."))
@@ -204,6 +207,7 @@
.allowDiagnosticWarningMessages()
.setMode(CompilationMode.RELEASE)
.addKeepMainRule(main)
+ .addDontWarnJetBrainsNotNullAnnotation()
.setMinApi(parameters.getApiLevel())
.compile()
.assertAllWarningMessagesMatch(equalTo("Resource 'META-INF/MANIFEST.MF' already exists."))
diff --git a/src/test/java/com/android/tools/r8/rewrite/ServiceLoaderMultipleCallsTest.java b/src/test/java/com/android/tools/r8/rewrite/ServiceLoaderMultipleCallsTest.java
index 688a96f..fd1a05a 100644
--- a/src/test/java/com/android/tools/r8/rewrite/ServiceLoaderMultipleCallsTest.java
+++ b/src/test/java/com/android/tools/r8/rewrite/ServiceLoaderMultipleCallsTest.java
@@ -4,11 +4,10 @@
package com.android.tools.r8.rewrite;
-import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
import static junit.framework.TestCase.assertEquals;
import static junit.framework.TestCase.assertNull;
import static junit.framework.TestCase.assertTrue;
-import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertFalse;
import com.android.tools.r8.CompilationFailedException;
import com.android.tools.r8.DataEntryResource;
@@ -20,6 +19,7 @@
import com.android.tools.r8.utils.StringUtils;
import com.android.tools.r8.utils.codeinspector.ClassSubject;
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 java.io.IOException;
import java.nio.file.Path;
@@ -109,11 +109,18 @@
inspector -> {
// Check that we have actually rewritten the calls to ServiceLoader.load.
assertEquals(0, getServiceLoaderLoads(inspector, MainRunner.class));
- // Check that the synthesize service loader class holds two methods, one for each
- // context.
- ClassSubject serviceLoaderMethods = inspector.clazz("$$ServiceLoaderMethods");
- assertThat(serviceLoaderMethods, isPresent());
- assertEquals(2, serviceLoaderMethods.allMethods().size());
+ // Check the synthesize service loader method is a single shared method.
+ // Due to minification we just check there is only a single synthetic class with a
+ // single static method.
+ boolean found = false;
+ for (FoundClassSubject clazz : inspector.allClasses()) {
+ if (clazz.isSynthetic()) {
+ assertFalse(found);
+ found = true;
+ assertEquals(1, clazz.allMethods().size());
+ clazz.forAllMethods(m -> assertTrue(m.isStatic()));
+ }
+ }
});
// Check that we have removed the service configuration from META-INF/services.
diff --git a/src/test/java/com/android/tools/r8/rewrite/enums/EnumInvalidValuesLengthTest.java b/src/test/java/com/android/tools/r8/rewrite/enums/EnumInvalidValuesLengthTest.java
index ead99d1..2636944 100644
--- a/src/test/java/com/android/tools/r8/rewrite/enums/EnumInvalidValuesLengthTest.java
+++ b/src/test/java/com/android/tools/r8/rewrite/enums/EnumInvalidValuesLengthTest.java
@@ -36,6 +36,7 @@
.addKeepMainRule(EnumInvalidValuesLengthTest.Main.class)
.addProgramClasses(EnumInvalidValuesLengthTest.Main.class)
.addProgramClassFileData(transformValues(MyEnum.class))
+ .enableNeverClassInliningAnnotations()
.setMinApi(parameters.getApiLevel())
.compile()
.run(parameters.getRuntime(), EnumInvalidValuesLengthTest.Main.class)
diff --git a/src/test/java/com/android/tools/r8/rewrite/enums/EnumOptimizationTest.java b/src/test/java/com/android/tools/r8/rewrite/enums/EnumOptimizationTest.java
index ae4947a..7733b23 100644
--- a/src/test/java/com/android/tools/r8/rewrite/enums/EnumOptimizationTest.java
+++ b/src/test/java/com/android/tools/r8/rewrite/enums/EnumOptimizationTest.java
@@ -57,6 +57,7 @@
testForR8(parameters.getBackend())
.addProgramClassesAndInnerClasses(Ordinals.class)
.addKeepMainRule(Ordinals.class)
+ .enableForceInliningAnnotations()
.enableInliningAnnotations()
.enableSideEffectAnnotations()
.addOptionsModification(this::configure)
@@ -105,6 +106,7 @@
testForR8(parameters.getBackend())
.addProgramClassesAndInnerClasses(Names.class)
.addKeepMainRule(Names.class)
+ .enableForceInliningAnnotations()
.enableInliningAnnotations()
.enableSideEffectAnnotations()
.addOptionsModification(this::configure)
@@ -150,6 +152,7 @@
testForR8(parameters.getBackend())
.addProgramClassesAndInnerClasses(ToStrings.class)
.addKeepMainRule(ToStrings.class)
+ .enableForceInliningAnnotations()
.enableInliningAnnotations()
.enableSideEffectAnnotations()
.addOptionsModification(this::configure)
diff --git a/src/test/java/com/android/tools/r8/rewrite/enums/EnumSideEffect.java b/src/test/java/com/android/tools/r8/rewrite/enums/EnumSideEffect.java
index a0e59b3..2d1b018 100644
--- a/src/test/java/com/android/tools/r8/rewrite/enums/EnumSideEffect.java
+++ b/src/test/java/com/android/tools/r8/rewrite/enums/EnumSideEffect.java
@@ -39,6 +39,7 @@
.addKeepMainRule(Main.class)
.enableInliningAnnotations()
.addInnerClasses(EnumSideEffect.class)
+ .enableNeverClassInliningAnnotations()
.setMinApi(parameters.getApiLevel())
.compile()
.run(parameters.getRuntime(), Main.class)
diff --git a/src/test/java/com/android/tools/r8/rewrite/serviceloaders/MissingServiceClassTest.java b/src/test/java/com/android/tools/r8/rewrite/serviceloaders/MissingServiceClassTest.java
index ee99ea1..5bcfdb8 100644
--- a/src/test/java/com/android/tools/r8/rewrite/serviceloaders/MissingServiceClassTest.java
+++ b/src/test/java/com/android/tools/r8/rewrite/serviceloaders/MissingServiceClassTest.java
@@ -5,7 +5,6 @@
package com.android.tools.r8.rewrite.serviceloaders;
import static com.android.tools.r8.DiagnosticsMatcher.diagnosticMessage;
-import static org.hamcrest.CoreMatchers.anyOf;
import static org.hamcrest.CoreMatchers.containsString;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
@@ -55,10 +54,11 @@
AppServices.SERVICE_DIRECTORY_NAME + Service.class.getTypeName(),
Origin.unknown()))
.addOptionsModification(o -> o.dataResourceConsumer = dataResourceConsumer)
+ .addDontWarn(ServiceImpl.class)
.allowDiagnosticWarningMessages()
.setMinApi(parameters.getApiLevel())
.compile()
- .inspectDiagnosticMessages(this::inspectDiagnosticMessages)
+ .inspectDiagnosticMessages(this::inspectDiagnosticMessagesWithDontWarn)
.addRunClasspathClasses(Service.class, ServiceImpl.class)
.run(parameters.getRuntime(), TestClass.class)
.assertSuccessWithEmptyOutput();
@@ -80,26 +80,39 @@
AppServices.SERVICE_DIRECTORY_NAME + Service.class.getTypeName(),
Origin.unknown()))
.run()
- .inspectDiagnosticMessages(this::inspectDiagnosticMessages);
+ .inspectDiagnosticMessages(this::inspectDiagnosticMessagesWithoutDontWarn);
}
- private void inspectDiagnosticMessages(TestDiagnosticMessages inspector) {
- inspector.assertWarningsCount(2);
- inspector.assertAllWarningsMatch(
- diagnosticMessage(
- anyOf(
+ private void inspectDiagnosticMessagesWithDontWarn(TestDiagnosticMessages diagnostics) {
+ diagnostics
+ .assertOnlyWarnings()
+ .assertWarningsMatch(
+ diagnosticMessage(
containsString(
"Unexpected reference to missing service class: "
+ AppServices.SERVICE_DIRECTORY_NAME
+ Service.class.getTypeName()
- + "."),
+ + ".")));
+ }
+
+ private void inspectDiagnosticMessagesWithoutDontWarn(TestDiagnosticMessages inspector) {
+ inspector
+ .assertOnlyWarnings()
+ .assertWarningsMatch(
+ diagnosticMessage(
+ containsString(
+ "Unexpected reference to missing service class: "
+ + AppServices.SERVICE_DIRECTORY_NAME
+ + Service.class.getTypeName()
+ + ".")),
+ diagnosticMessage(
containsString(
"Unexpected reference to missing service implementation class in "
+ AppServices.SERVICE_DIRECTORY_NAME
+ Service.class.getTypeName()
+ ": "
+ ServiceImpl.class.getTypeName()
- + "."))));
+ + ".")));
}
private void inspectResource(List<String> contents) {
diff --git a/src/test/java/com/android/tools/r8/rewrite/serviceloaders/MissingServiceClassWithFeatureTest.java b/src/test/java/com/android/tools/r8/rewrite/serviceloaders/MissingServiceClassWithFeatureTest.java
index f36c122..be5dfb6 100644
--- a/src/test/java/com/android/tools/r8/rewrite/serviceloaders/MissingServiceClassWithFeatureTest.java
+++ b/src/test/java/com/android/tools/r8/rewrite/serviceloaders/MissingServiceClassWithFeatureTest.java
@@ -38,7 +38,7 @@
.addProgramClasses(TestClass.class, ServiceImpl.class)
.addKeepMainRule(TestClass.class)
.addKeepClassAndMembersRules(FeatureClass.class)
- .addKeepRules("-dontwarn " + Service.class.getTypeName())
+ .addDontWarn(Service.class.getTypeName())
.addDataEntryResources(
DataEntryResource.fromBytes(
StringUtils.lines("java.lang.Object").getBytes(),
diff --git a/src/test/java/com/android/tools/r8/rewrite/serviceloaders/MissingServiceImplementationClassTest.java b/src/test/java/com/android/tools/r8/rewrite/serviceloaders/MissingServiceImplementationClassTest.java
index 9039c50..ca17780 100644
--- a/src/test/java/com/android/tools/r8/rewrite/serviceloaders/MissingServiceImplementationClassTest.java
+++ b/src/test/java/com/android/tools/r8/rewrite/serviceloaders/MissingServiceImplementationClassTest.java
@@ -4,15 +4,12 @@
package com.android.tools.r8.rewrite.serviceloaders;
-import static com.android.tools.r8.DiagnosticsMatcher.diagnosticMessage;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
-import static org.hamcrest.CoreMatchers.containsString;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import com.android.tools.r8.DataEntryResource;
-import com.android.tools.r8.NeverInline;
import com.android.tools.r8.R8TestCompileResult;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
@@ -59,6 +56,7 @@
R8TestCompileResult compileResult =
testForR8(parameters.getBackend())
.addProgramClasses(TestClass.class, Service.class)
+ .addDontWarn(ServiceImpl.class)
.addKeepMainRule(TestClass.class)
.addKeepClassAndMembersRulesWithAllowObfuscation(Service.class)
.addDataEntryResources(
@@ -72,22 +70,8 @@
new DataResourceConsumerForTesting(options.dataResourceConsumer));
options.dataResourceConsumer = dataResourceConsumer.get();
})
- .allowDiagnosticWarningMessages()
.setMinApi(parameters.getApiLevel())
- .compile()
- .inspectDiagnosticMessages(
- inspector -> {
- inspector.assertWarningsCount(1);
- inspector.assertAllWarningsMatch(
- diagnosticMessage(
- containsString(
- "Unexpected reference to missing service implementation class in "
- + AppServices.SERVICE_DIRECTORY_NAME
- + Service.class.getTypeName()
- + ": "
- + ServiceImpl.class.getTypeName()
- + ".")));
- });
+ .compile();
CodeInspector inspector = compileResult.inspector();
ClassSubject serviceClassSubject = inspector.clazz(Service.class);
@@ -171,7 +155,6 @@
public static class ServiceImpl implements Service {
@Override
- @NeverInline
public void greet() {
System.out.println("Hello world!");
}
diff --git a/src/test/java/com/android/tools/r8/rewrite/serviceloaders/MissingServiceImplementationClassWithFeatureTest.java b/src/test/java/com/android/tools/r8/rewrite/serviceloaders/MissingServiceImplementationClassWithFeatureTest.java
index b3f120c..a3aea84 100644
--- a/src/test/java/com/android/tools/r8/rewrite/serviceloaders/MissingServiceImplementationClassWithFeatureTest.java
+++ b/src/test/java/com/android/tools/r8/rewrite/serviceloaders/MissingServiceImplementationClassWithFeatureTest.java
@@ -4,8 +4,6 @@
package com.android.tools.r8.rewrite.serviceloaders;
-import static com.android.tools.r8.DiagnosticsMatcher.diagnosticMessage;
-import static org.hamcrest.CoreMatchers.containsString;
import com.android.tools.r8.DataEntryResource;
import com.android.tools.r8.TestBase;
@@ -40,6 +38,7 @@
public void testR8() throws Exception {
testForR8(parameters.getBackend())
.addProgramClasses(TestClass.class, Service.class)
+ .addDontWarn("MissingClass")
.addKeepMainRule(TestClass.class)
.addKeepClassAndMembersRules(FeatureClass.class)
.addDataEntryResources(
@@ -48,20 +47,8 @@
AppServices.SERVICE_DIRECTORY_NAME + Service.class.getTypeName(),
Origin.unknown()))
.addFeatureSplit(FeatureClass.class)
- .allowDiagnosticWarningMessages()
.setMinApi(parameters.getApiLevel())
- .compile()
- .inspectDiagnosticMessages(
- inspector -> {
- inspector.assertWarningsCount(1);
- inspector.assertAllWarningsMatch(
- diagnosticMessage(
- containsString(
- "Unexpected reference to missing service implementation class in "
- + AppServices.SERVICE_DIRECTORY_NAME
- + Service.class.getTypeName()
- + ": MissingClass.")));
- });
+ .compile();
}
static class TestClass {
diff --git a/src/test/java/com/android/tools/r8/rewrite/serviceloaders/ServiceWithoutResourceFileAtCompileTimeTest.java b/src/test/java/com/android/tools/r8/rewrite/serviceloaders/ServiceWithoutResourceFileAtCompileTimeTest.java
index 1ea55b8..9f5eada 100644
--- a/src/test/java/com/android/tools/r8/rewrite/serviceloaders/ServiceWithoutResourceFileAtCompileTimeTest.java
+++ b/src/test/java/com/android/tools/r8/rewrite/serviceloaders/ServiceWithoutResourceFileAtCompileTimeTest.java
@@ -69,6 +69,7 @@
.addClasspathClasses(Service.class)
.addKeepAllClassesRule()
.addApplyMapping(compileResult.getProguardMap())
+ .enableInliningAnnotations()
.setMinApi(parameters.getApiLevel())
.compile()
.addRunClasspathFiles(
diff --git a/src/test/java/com/android/tools/r8/rewrite/switches/MaxIntSwitchTest.java b/src/test/java/com/android/tools/r8/rewrite/switches/MaxIntSwitchTest.java
new file mode 100644
index 0000000..0f3f2cc
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/rewrite/switches/MaxIntSwitchTest.java
@@ -0,0 +1,90 @@
+// 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.rewrite.switches;
+
+import com.android.tools.r8.CompilationMode;
+import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.NeverPropagateValue;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestRunResult;
+import com.android.tools.r8.ToolHelper.DexVm.Version;
+import java.util.List;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class MaxIntSwitchTest extends TestBase {
+
+ private final TestParameters parameters;
+ private final CompilationMode mode;
+
+ @Parameterized.Parameters(name = "{0}, mode = {1}")
+ public static List<Object[]> data() {
+ return buildParameters(
+ getTestParameters().withDexRuntimes().withAllApiLevels().build(), CompilationMode.values());
+ }
+
+ // See b/177790310 for details.
+ public MaxIntSwitchTest(TestParameters parameters, CompilationMode mode) {
+ this.parameters = parameters;
+ this.mode = mode;
+ }
+
+ private void checkResult(TestRunResult<?> result) {
+ if (parameters.getDexRuntimeVersion().isOlderThanOrEqual(Version.V4_0_4)) {
+ result.assertFailureWithErrorThatThrows(AssertionError.class);
+ } else {
+ result.assertSuccessWithOutputLines("good");
+ }
+ }
+
+ @Test
+ public void testD8() throws Exception {
+ testForD8(parameters.getBackend())
+ .addInnerClasses(this.getClass())
+ .setMinApi(parameters.getApiLevel())
+ .setMode(mode)
+ .run(parameters.getRuntime(), TestClass.class)
+ .apply(this::checkResult);
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ testForR8(parameters.getBackend())
+ .addInnerClasses(this.getClass())
+ .addKeepMainRule(TestClass.class)
+ .enableInliningAnnotations()
+ .enableMemberValuePropagationAnnotations()
+ .setMinApi(parameters.getApiLevel())
+ .setMode(mode)
+ .run(parameters.getRuntime(), TestClass.class)
+ .apply(this::checkResult);
+ }
+
+ static class TestClass {
+ @NeverInline
+ @NeverPropagateValue
+ public static void f(int i) {
+ switch (i) {
+ case 0x7ffffffd:
+ System.out.println("a");
+ break;
+ case 0x7ffffffe:
+ System.out.println("b");
+ break;
+ case 0x7fffffff:
+ System.out.println("good");
+ break;
+ default:
+ throw new AssertionError();
+ }
+ }
+
+ public static void main(String[] args) {
+ f(0x7fffffff);
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/shaking/KeepAnnotatedMemberTest.java b/src/test/java/com/android/tools/r8/shaking/KeepAnnotatedMemberTest.java
index b5aba75..993a658 100644
--- a/src/test/java/com/android/tools/r8/shaking/KeepAnnotatedMemberTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/KeepAnnotatedMemberTest.java
@@ -18,6 +18,7 @@
import com.android.tools.r8.ToolHelper;
import com.android.tools.r8.utils.codeinspector.ClassSubject;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import com.android.tools.r8.utils.codeinspector.FoundClassSubject;
import com.android.tools.r8.utils.codeinspector.FoundMethodSubject;
import com.android.tools.r8.utils.codeinspector.MethodSubject;
import com.android.tools.r8.utils.graphinspector.GraphInspector;
@@ -73,6 +74,9 @@
testForR8(Backend.CF)
.addProgramFiles(R8_JAR)
.addKeepRules("-keep class * { @" + PRESENT_ANNOTATION + " *; }")
+ .addDontWarnGoogle()
+ .addDontWarnJavax()
+ .addDontWarn("org.codehaus.mojo.animal_sniffer.IgnoreJRERequirement")
.compileWithExpectedDiagnostics(
diagnostics ->
diagnostics.assertErrorsMatch(diagnosticException(AssertionError.class)));
@@ -85,6 +89,9 @@
.addProgramFiles(R8_JAR)
.addKeepRules(
"-keep class *", "-keepclassmembers class * { @" + PRESENT_ANNOTATION + " *; }")
+ .addDontWarnGoogle()
+ .addDontWarnJavax()
+ .addDontWarn("org.codehaus.mojo.animal_sniffer.IgnoreJRERequirement")
.compile();
}
@@ -103,6 +110,9 @@
testForR8(Backend.CF)
.addProgramFiles(R8_JAR)
.addKeepRules("-keepclasseswithmembers class * { @" + PRESENT_ANNOTATION + " *** *(...); }")
+ .addDontWarnGoogle()
+ .addDontWarnJavax()
+ .addDontWarn("org.codehaus.mojo.animal_sniffer.IgnoreJRERequirement")
.compile()
.inspect(
inspector -> {
@@ -192,6 +202,8 @@
// TODO(b/132318609): Remove keep annotation once fixed.
.addKeepClassRules(PRESENT_ANNOTATION)
.addKeepRules("-keepclassmembers class * { @" + PRESENT_ANNOTATION + " *** *(...); }")
+ .addDontWarnGoogle()
+ .addDontWarnJavaxNullableAnnotation()
.compile()
.graphInspector();
@@ -208,6 +220,8 @@
+ "-keepclassmembers class <1> { @"
+ PRESENT_ANNOTATION
+ " *** *(...); }")
+ .addDontWarnGoogle()
+ .addDontWarnJavaxNullableAnnotation()
.compile()
.graphInspector();
assertRetainedClassesEqual(referenceInspector, ifThenKeepClassMembersInspector, false, false);
@@ -225,6 +239,8 @@
+ "-keepclasseswithmembers class <1> { @"
+ PRESENT_ANNOTATION
+ " *** *(...); }")
+ .addDontWarnGoogle()
+ .addDontWarnJavaxNullableAnnotation()
.compile()
.graphInspector();
assertRetainedClassesEqual(
@@ -245,6 +261,8 @@
+ "-keep class <1> { @"
+ PRESENT_ANNOTATION
+ " *** <2>(...); }")
+ .addDontWarnGoogle()
+ .addDontWarnJavaxNullableAnnotation()
.compile()
.graphInspector();
// TODO(b/159418523): Should the reference be equal to the result with the conditional rule?
@@ -259,12 +277,12 @@
Set<String> referenceClasses =
new TreeSet<>(
referenceResult.codeInspector().allClasses().stream()
- .map(c -> c.getOriginalName())
+ .map(FoundClassSubject::getOriginalName)
.collect(Collectors.toSet()));
Set<String> conditionalClasses =
conditionalResult.codeInspector().allClasses().stream()
- .map(c -> c.getOriginalName())
+ .map(FoundClassSubject::getOriginalName)
.collect(Collectors.toSet());
{
SetView<String> notInReference = Sets.difference(conditionalClasses, referenceClasses);
diff --git a/src/test/java/com/android/tools/r8/shaking/LibraryMethodOverrideInLambdaMarkingTest.java b/src/test/java/com/android/tools/r8/shaking/LibraryMethodOverrideInLambdaMarkingTest.java
index 7d68070..331eb12 100644
--- a/src/test/java/com/android/tools/r8/shaking/LibraryMethodOverrideInLambdaMarkingTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/LibraryMethodOverrideInLambdaMarkingTest.java
@@ -87,14 +87,12 @@
}
}
- @FunctionalInterface
@NoVerticalClassMerging
interface I {
Iterator<Object> iterator();
}
- @FunctionalInterface
@NoVerticalClassMerging
interface J extends Iterable<Object> {
diff --git a/src/test/java/com/android/tools/r8/shaking/MissingInterfaceTest.java b/src/test/java/com/android/tools/r8/shaking/MissingInterfaceTest.java
index 7254ee6..617676f 100644
--- a/src/test/java/com/android/tools/r8/shaking/MissingInterfaceTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/MissingInterfaceTest.java
@@ -35,11 +35,10 @@
public void test_missingInterface() throws Exception {
testForR8(parameters.getBackend())
.addProgramClasses(TestClassForB112849320.class)
+ .addDontWarn(GoingToBeMissed.class)
.setMinApi(parameters.getApiLevel())
.addKeepMainRule(TestClassForB112849320.class)
.addOptionsModification(options -> options.enableInlining = false)
- .allowDiagnosticWarningMessages(
- parameters.isCfRuntime() || !parameters.canUseDefaultAndStaticInterfaceMethods())
.compile()
.addRunClasspathFiles(
buildOnDexRuntime(
diff --git a/src/test/java/com/android/tools/r8/shaking/PreserveDesugaredLambdaTest.java b/src/test/java/com/android/tools/r8/shaking/PreserveDesugaredLambdaTest.java
index cd153f2..74b7c8a 100644
--- a/src/test/java/com/android/tools/r8/shaking/PreserveDesugaredLambdaTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/PreserveDesugaredLambdaTest.java
@@ -13,7 +13,6 @@
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
-import com.android.tools.r8.utils.AndroidApiLevel;
import java.io.IOException;
import java.util.concurrent.ExecutionException;
import org.junit.Test;
@@ -68,8 +67,7 @@
.addClasspathClasses(Interface.class)
.addLibraryFiles(TestBase.runtimeJar(parameters.getBackend()))
.addKeepAllClassesRule()
- .allowDiagnosticWarningMessages(
- parameters.isDexRuntime() && parameters.getApiLevel().isLessThan(AndroidApiLevel.N))
+ .addDontWarn(A.class)
.setMinApi(parameters.getApiLevel())
.compile()
.addRunClasspathFiles(libraryCompileResult.writeToZip())
@@ -79,8 +77,7 @@
codeInspector.allClasses().stream()
.anyMatch(
c -> {
- if (c.getOriginalName()
- .contains("-$$Lambda$PreserveDesugaredLambdaTest$Main")) {
+ if (c.isSynthesizedJavaLambdaClass()) {
assertThat(c.uniqueMethodWithName("computeTheFoo"), isPresent());
return true;
}
diff --git a/src/test/java/com/android/tools/r8/shaking/PrintUsageTest.java b/src/test/java/com/android/tools/r8/shaking/PrintUsageTest.java
index 9c88b7f..3d2e137 100644
--- a/src/test/java/com/android/tools/r8/shaking/PrintUsageTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/PrintUsageTest.java
@@ -7,8 +7,10 @@
import static org.junit.Assert.assertTrue;
import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
import com.android.tools.r8.ToolHelper;
import com.android.tools.r8.shaking.PrintUsageTest.PrintUsageInspector.ClassSubject;
+import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.ListUtils;
import com.google.common.collect.ImmutableList;
import java.io.File;
@@ -38,18 +40,18 @@
private static final String PRINT_USAGE_FILE_SUFFIX = "-print-usage.txt";
- private final Backend backend;
+ private final TestParameters parameters;
private final String test;
private final String programFile;
private final List<String> keepRulesFiles;
private final Consumer<PrintUsageInspector> inspection;
public PrintUsageTest(
- Backend backend,
+ TestParameters parameters,
String test,
List<String> keepRulesFiles,
Consumer<PrintUsageInspector> inspection) {
- this.backend = backend;
+ this.parameters = parameters;
this.test = test;
this.programFile = ToolHelper.EXAMPLES_BUILD_DIR + test + ".jar";
this.keepRulesFiles = keepRulesFiles;
@@ -59,13 +61,19 @@
@Before
public void runR8andGetPrintUsage() throws Exception {
Path out = temp.getRoot().toPath();
- testForR8(backend)
+ testForR8(parameters.getBackend())
.addProgramFiles(Paths.get(programFile))
.addKeepRuleFiles(ListUtils.map(keepRulesFiles, Paths::get))
.addKeepRules("-printusage " + out.resolve(test + PRINT_USAGE_FILE_SUFFIX))
+ .applyIf(
+ test.equals("shaking12")
+ && parameters.isDexRuntime()
+ && parameters.getApiLevel().isLessThan(AndroidApiLevel.K),
+ builder -> builder.addDontWarn(ReflectiveOperationException.class))
// Disable inlining to make this test not depend on inlining decisions.
.addOptionsModification(o -> o.enableInlining = false)
.enableProguardTestOptions()
+ .setMinApi(parameters.getApiLevel())
.compile();
}
@@ -79,7 +87,7 @@
}
}
- @Parameters(name = "test: {0} keep: {1}")
+ @Parameters(name = "{0}, test: {1} keep: {2}")
public static Collection<Object[]> data() {
List<String> tests = Arrays.asList(
"shaking1", "shaking2", "shaking4", "shaking8", "shaking9", "shaking12");
@@ -93,7 +101,7 @@
inspections.put("shaking12:keep-rules-printusage.txt", PrintUsageTest::inspectShaking12);
List<Object[]> testCases = new ArrayList<>();
- for (Backend backend : ToolHelper.getBackends()) {
+ for (TestParameters parameters : getTestParameters().withAllRuntimesAndApiLevels().build()) {
Set<String> usedInspections = new HashSet<>();
for (String test : tests) {
File[] keepFiles = new File(ToolHelper.EXAMPLES_DIR + test)
@@ -103,8 +111,8 @@
Consumer<PrintUsageInspector> inspection =
getTestOptionalParameter(inspections, usedInspections, test, keepName);
if (inspection != null) {
- testCases.add(
- new Object[] {backend, test, ImmutableList.of(keepFile.getPath()), inspection});
+ testCases.add(
+ new Object[] {parameters, test, ImmutableList.of(keepFile.getPath()), inspection});
}
}
}
diff --git a/src/test/java/com/android/tools/r8/shaking/ProguardConfigurationParserTest.java b/src/test/java/com/android/tools/r8/shaking/ProguardConfigurationParserTest.java
index c15fb48..b479f43 100644
--- a/src/test/java/com/android/tools/r8/shaking/ProguardConfigurationParserTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/ProguardConfigurationParserTest.java
@@ -22,6 +22,7 @@
import com.android.tools.r8.TestParametersCollection;
import com.android.tools.r8.ToolHelper;
import com.android.tools.r8.ToolHelper.ProcessResult;
+import com.android.tools.r8.errors.dontwarn.DontWarnConfiguration;
import com.android.tools.r8.graph.ClassAccessFlags;
import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.graph.DexType;
@@ -56,6 +57,7 @@
import java.util.Optional;
import java.util.function.BiConsumer;
import java.util.function.Function;
+import java.util.function.Predicate;
import java.util.function.Supplier;
import java.util.regex.Matcher;
import java.util.stream.Collectors;
@@ -268,7 +270,7 @@
}
private void testDontXXX(
- String xxx, Function<ProguardConfiguration, ProguardClassFilter> pattern) {
+ String xxx, Function<ProguardConfiguration, Predicate<DexType>> matcherFactory) {
DexItemFactory dexItemFactory = new DexItemFactory();
ProguardConfigurationParser parser =
new ProguardConfigurationParser(dexItemFactory, reporter);
@@ -276,19 +278,20 @@
parser.parse(createConfigurationForTesting(ImmutableList.of(configuration)));
verifyParserEndsCleanly();
ProguardConfiguration config = parser.getConfig();
- assertFalse(pattern.apply(config).matches(dexItemFactory.createType("Lboobaz;")));
- assertTrue(pattern.apply(config).matches(dexItemFactory.createType("Lboobar;")));
- assertFalse(pattern.apply(config).matches(dexItemFactory.createType("Lfoobar;")));
+ Predicate<DexType> matcher = matcherFactory.apply(config);
+ assertFalse(matcher.test(dexItemFactory.createType("Lboobaz;")));
+ assertTrue(matcher.test(dexItemFactory.createType("Lboobar;")));
+ assertFalse(matcher.test(dexItemFactory.createType("Lfoobar;")));
}
@Test
public void testDontXXX() {
- testDontXXX("warn", ProguardConfiguration::getDontWarnPatterns);
- testDontXXX("note", ProguardConfiguration::getDontNotePatterns);
+ testDontXXX("warn", config -> DontWarnConfiguration.create(config)::matches);
+ testDontXXX("note", config -> config.getDontNotePatterns()::matches);
}
private void testDontXXXMultiple(
- String xxx, Function<ProguardConfiguration, ProguardClassFilter> pattern) {
+ String xxx, Function<ProguardConfiguration, Predicate<DexType>> matcherFactory) {
DexItemFactory dexItemFactory = new DexItemFactory();
ProguardConfigurationParser parser =
new ProguardConfigurationParser(dexItemFactory, reporter);
@@ -298,20 +301,21 @@
parser.parse(createConfigurationForTesting(configuration));
verifyParserEndsCleanly();
ProguardConfiguration config = parser.getConfig();
- assertTrue(pattern.apply(config).matches(dexItemFactory.createType("Lfoo/Bar;")));
- assertTrue(pattern.apply(config).matches(dexItemFactory.createType("Lfoo/bar7Bar;")));
- assertTrue(pattern.apply(config).matches(dexItemFactory.createType("Lbar/Foo;")));
+ Predicate<DexType> matcher = matcherFactory.apply(config);
+ assertTrue(matcher.test(dexItemFactory.createType("Lfoo/Bar;")));
+ assertTrue(matcher.test(dexItemFactory.createType("Lfoo/bar7Bar;")));
+ assertTrue(matcher.test(dexItemFactory.createType("Lbar/Foo;")));
}
}
@Test
public void testDontWarnMultiple() {
- testDontXXXMultiple("warn", ProguardConfiguration::getDontWarnPatterns);
- testDontXXXMultiple("note", ProguardConfiguration::getDontNotePatterns);
+ testDontXXXMultiple("warn", config -> DontWarnConfiguration.create(config)::matches);
+ testDontXXXMultiple("note", config -> config.getDontNotePatterns()::matches);
}
private void testDontXXXAllExplicitly(
- String xxx, Function<ProguardConfiguration, ProguardClassFilter> pattern) {
+ String xxx, Function<ProguardConfiguration, Predicate<DexType>> matcherFactory) {
DexItemFactory dexItemFactory = new DexItemFactory();
ProguardConfigurationParser parser =
new ProguardConfigurationParser(dexItemFactory, reporter);
@@ -319,19 +323,20 @@
parser.parse(createConfigurationForTesting(ImmutableList.of(dontwarnAll)));
verifyParserEndsCleanly();
ProguardConfiguration config = parser.getConfig();
- assertTrue(pattern.apply(config).matches(dexItemFactory.createType("Lboobaz;")));
- assertTrue(pattern.apply(config).matches(dexItemFactory.createType("Lboobar;")));
- assertTrue(pattern.apply(config).matches(dexItemFactory.createType("Lfoobar;")));
+ Predicate<DexType> matcher = matcherFactory.apply(config);
+ assertTrue(matcher.test(dexItemFactory.createType("Lboobaz;")));
+ assertTrue(matcher.test(dexItemFactory.createType("Lboobar;")));
+ assertTrue(matcher.test(dexItemFactory.createType("Lfoobar;")));
}
@Test
public void testDontWarnAllExplicitly() {
- testDontXXXAllExplicitly("warn", ProguardConfiguration::getDontWarnPatterns);
- testDontXXXAllExplicitly("note", ProguardConfiguration::getDontNotePatterns);
+ testDontXXXAllExplicitly("warn", config -> DontWarnConfiguration.create(config)::matches);
+ testDontXXXAllExplicitly("note", config -> config.getDontNotePatterns()::matches);
}
private void testDontXXXAllImplicitly(
- String xxx, Function<ProguardConfiguration, ProguardClassFilter> pattern) {
+ String xxx, Function<ProguardConfiguration, Predicate<DexType>> matcherFactory) {
DexItemFactory dexItemFactory = new DexItemFactory();
ProguardConfigurationParser parser =
new ProguardConfigurationParser(dexItemFactory, reporter);
@@ -339,15 +344,16 @@
String otherOption = "-keep class *";
parser.parse(createConfigurationForTesting(ImmutableList.of(dontwarnAll, otherOption)));
ProguardConfiguration config = parser.getConfig();
- assertTrue(pattern.apply(config).matches(dexItemFactory.createType("Lboobaz;")));
- assertTrue(pattern.apply(config).matches(dexItemFactory.createType("Lboobar;")));
- assertTrue(pattern.apply(config).matches(dexItemFactory.createType("Lfoobar;")));
+ Predicate<DexType> matcher = matcherFactory.apply(config);
+ assertTrue(matcher.test(dexItemFactory.createType("Lboobaz;")));
+ assertTrue(matcher.test(dexItemFactory.createType("Lboobar;")));
+ assertTrue(matcher.test(dexItemFactory.createType("Lfoobar;")));
}
@Test
public void testDontWarnAllImplicitly() {
- testDontXXXAllImplicitly("warn", ProguardConfiguration::getDontWarnPatterns);
- testDontXXXAllImplicitly("note", ProguardConfiguration::getDontNotePatterns);
+ testDontXXXAllImplicitly("warn", config -> DontWarnConfiguration.create(config)::matches);
+ testDontXXXAllImplicitly("note", config -> config.getDontNotePatterns()::matches);
}
@Test
diff --git a/src/test/java/com/android/tools/r8/shaking/TreeShakingTest.java b/src/test/java/com/android/tools/r8/shaking/TreeShakingTest.java
index 96c6fb3..2f1a112 100644
--- a/src/test/java/com/android/tools/r8/shaking/TreeShakingTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/TreeShakingTest.java
@@ -152,6 +152,24 @@
BiConsumer<CodeInspector, CodeInspector> dexComparator,
List<String> keepRulesFiles,
Consumer<InternalOptions> optionsConsumer,
+ ThrowableConsumer<R8FullTestBuilder> testBuilderConsumer)
+ throws Exception {
+ runTest(
+ inspection,
+ outputComparator,
+ dexComparator,
+ keepRulesFiles,
+ optionsConsumer,
+ testBuilderConsumer,
+ null);
+ }
+
+ protected void runTest(
+ ThrowingConsumer<CodeInspector, Exception> inspection,
+ BiConsumer<String, String> outputComparator,
+ BiConsumer<CodeInspector, CodeInspector> dexComparator,
+ List<String> keepRulesFiles,
+ Consumer<InternalOptions> optionsConsumer,
ThrowableConsumer<R8FullTestBuilder> testBuilderConsumer,
DiagnosticsConsumer diagnosticsConsumer)
throws Exception {
@@ -179,6 +197,9 @@
if (optionsConsumer != null) {
optionsConsumer.accept(options);
}
+ if (frontend == Frontend.DEX) {
+ options.testing.allowDexInputForTesting = true;
+ }
})
.allowStdoutMessages()
.applyIf(testBuilderConsumer != null, testBuilderConsumer);
diff --git a/src/test/java/com/android/tools/r8/shaking/annotations/AnnotationShakingBehaviorTest.java b/src/test/java/com/android/tools/r8/shaking/annotations/AnnotationShakingBehaviorTest.java
index 484fb10..9c98c6e 100644
--- a/src/test/java/com/android/tools/r8/shaking/annotations/AnnotationShakingBehaviorTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/annotations/AnnotationShakingBehaviorTest.java
@@ -91,6 +91,8 @@
.addKeepMainRule(MainWithNewB.class)
.addKeepClassAndMembersRules(Factory.class)
.addKeepAttributes("*Annotation*")
+ .enableInliningAnnotations()
+ .enableNeverClassInliningAnnotations()
.setMinApi(parameters.getApiLevel())
.run(parameters.getRuntime(), MainWithNewB.class)
.assertSuccessWithOutputLines("Hello World!")
diff --git a/src/test/java/com/android/tools/r8/shaking/annotations/LibraryAndMissingAnnotationsTest.java b/src/test/java/com/android/tools/r8/shaking/annotations/LibraryAndMissingAnnotationsTest.java
index 4e6adad..904c0cd 100644
--- a/src/test/java/com/android/tools/r8/shaking/annotations/LibraryAndMissingAnnotationsTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/annotations/LibraryAndMissingAnnotationsTest.java
@@ -107,7 +107,9 @@
.addKeepAttributes("*Annotation*")
.addLibraryFiles(runtimeJar(parameters))
.addKeepClassAndMembersRules(Foo.class)
- .addKeepRules("-dontwarn " + LibraryAndMissingAnnotationsTest.class.getTypeName())
+ .applyIf(
+ builder.isProguardTestBuilder(),
+ ignore -> builder.addDontWarn(LibraryAndMissingAnnotationsTest.class))
.addKeepMainRule(mainClass)
.setMinApi(parameters.getApiLevel());
if (includeOnLibraryPath) {
diff --git a/src/test/java/com/android/tools/r8/shaking/annotations/ProgramAnnotationRemovalTest.java b/src/test/java/com/android/tools/r8/shaking/annotations/ProgramAnnotationRemovalTest.java
index 3396686..13fbb3e 100644
--- a/src/test/java/com/android/tools/r8/shaking/annotations/ProgramAnnotationRemovalTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/annotations/ProgramAnnotationRemovalTest.java
@@ -49,6 +49,7 @@
.addInnerClasses(ProgramAnnotationRemovalTest.class)
.addKeepMainRule(TestClass.class)
.addKeepAttributes("RuntimeVisibleAnnotations")
+ .enableInliningAnnotations()
.setMinApi(parameters.getApiLevel())
.compile()
.run(parameters.getRuntime(), TestClass.class);
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 9c358e8..bc55c55 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
@@ -106,6 +106,7 @@
.addKeepMainRule(MAIN_CLASS_NAME)
.addKeepRules(KEEP_ANNOTATIONS)
.addKeepRules("-keep @interface " + ANNOTATION_NAME + " {", " *;", "}")
+ .addDontWarnJetBrainsAnnotations()
.allowDiagnosticWarningMessages()
.minification(minify)
.setMinApi(parameters.getApiLevel())
@@ -145,6 +146,7 @@
"-keep,allowobfuscation @interface " + ANNOTATION_NAME + " {",
" java.lang.String *f2();",
"}")
+ .addDontWarnJetBrainsAnnotations()
.allowDiagnosticWarningMessages()
.minification(minify)
.setMinApi(parameters.getApiLevel())
@@ -181,6 +183,7 @@
.addProgramFiles(getJavaJarFile(FOLDER))
.addKeepMainRule(MAIN_CLASS_NAME)
.addKeepRules(KEEP_ANNOTATIONS)
+ .addDontWarnJetBrainsAnnotations()
.allowDiagnosticWarningMessages()
.minification(minify)
.setMinApi(parameters.getApiLevel())
@@ -216,6 +219,7 @@
.addProgramFiles(compiledJars.getForConfiguration(kotlinc, targetVersion))
.addProgramFiles(getJavaJarFile(FOLDER))
.addKeepMainRule(MAIN_CLASS_NAME)
+ .addDontWarnJetBrainsAnnotations()
.allowDiagnosticWarningMessages()
.minification(minify)
.setMinApi(parameters.getApiLevel())
diff --git a/src/test/java/com/android/tools/r8/shaking/annotations/b137392797/B137392797.java b/src/test/java/com/android/tools/r8/shaking/annotations/b137392797/B137392797.java
index 3b9c067..1112ccd 100644
--- a/src/test/java/com/android/tools/r8/shaking/annotations/b137392797/B137392797.java
+++ b/src/test/java/com/android/tools/r8/shaking/annotations/b137392797/B137392797.java
@@ -5,13 +5,11 @@
package com.android.tools.r8.shaking.annotations.b137392797;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
-import static org.hamcrest.CoreMatchers.containsString;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.Assert.assertEquals;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
-import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.BooleanUtils;
import com.android.tools.r8.utils.codeinspector.ClassSubject;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
@@ -64,16 +62,14 @@
classWireFieldLabel(),
classTest(defaultEnumValueInAnnotation))
.addProgramClasses(TestClass.class)
+ .addDontWarnKotlin()
+ .addDontWarnJetBrainsAnnotations()
.addKeepClassAndMembersRules(
"com.squareup.wire.WireField", "com.squareup.demo.myapplication.Test")
.addKeepMainRule(TestClass.class)
.addKeepAttributes("*Annotation*")
- .allowDiagnosticWarningMessages(
- parameters.isDexRuntime() && parameters.getApiLevel().isLessThan(AndroidApiLevel.N))
.setMinApi(parameters.getApiLevel())
.compile()
- .assertAllWarningMessagesMatch(
- containsString("required for default or static interface methods desugaring"))
.inspect(this::checkEnumUses)
.run(parameters.getRuntime(), TestClass.class, "com.squareup.demo.myapplication.Test")
.assertSuccessWithOutputLines(
diff --git a/src/test/java/com/android/tools/r8/shaking/assumevalues/SynthesizedRulesFromApiLevelTest.java b/src/test/java/com/android/tools/r8/shaking/assumevalues/SynthesizedRulesFromApiLevelTest.java
index 0d9fa5e..b2f0964 100644
--- a/src/test/java/com/android/tools/r8/shaking/assumevalues/SynthesizedRulesFromApiLevelTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/assumevalues/SynthesizedRulesFromApiLevelTest.java
@@ -53,12 +53,12 @@
}
// Simple mock implementation of class android.os.Build$VERSION with just the SDK_INT field.
- private Path mockAndroidRuntimeLibrary(int sdkInt) throws Exception {
+ private Path mockAndroidRuntimeLibrary(AndroidApiLevel apiLevel) throws Exception {
JasminBuilder builder = new JasminBuilder();
ClassBuilder classBuilder;
classBuilder = builder.addClass("android.os.Build$VERSION");
- classBuilder.addStaticFinalField("SDK_INT", "I", Integer.toString(sdkInt));
+ classBuilder.addStaticFinalField("SDK_INT", "I", Integer.toString(apiLevel.getLevel()));
classBuilder = builder.addClass("android.os.Native");
classBuilder.addStaticMethod("method", ImmutableList.of(), "V",
@@ -71,13 +71,12 @@
return writeToJar(builder);
}
- private Path buildMockAndroidRuntimeLibrary(AndroidApiLevel apiLevel) throws Exception {
+ private Path buildMockAndroidRuntimeLibrary(Path mockAndroidRuntimeLibrary) throws Exception {
// Build the mock library containing android.os.Build.VERSION with D8.
Path library = temp.newFolder().toPath().resolve("library.jar");
D8.run(
- D8Command
- .builder()
- .addProgramFiles(mockAndroidRuntimeLibrary(apiLevel.getLevel()))
+ D8Command.builder()
+ .addProgramFiles(mockAndroidRuntimeLibrary)
.setOutput(library, OutputMode.DexIndexed)
.build());
return library;
@@ -164,9 +163,11 @@
throws Exception {
assertTrue(runtimeApiLevel.getLevel() >= buildApiLevel.getLevel());
if (backend == Backend.DEX) {
+ Path androidRuntimeLibraryMock = mockAndroidRuntimeLibrary(runtimeApiLevel);
testForR8(backend)
.setMinApi(buildApiLevel)
.addProgramFiles(buildApp(nativeApiLevel))
+ .addClasspathFiles(androidRuntimeLibraryMock)
.enableProguardTestOptions()
.addKeepRules("-neverinline class " + compatLibraryClassName + " { *; }")
.addKeepMainRule(mainClassName)
@@ -177,20 +178,21 @@
syntheticProguardRules ->
checkSynthesizedRuleExpectation(syntheticProguardRules, synthesizedRule))
.inspect(inspector)
- .addRunClasspathFiles(ImmutableList.of(buildMockAndroidRuntimeLibrary(runtimeApiLevel)))
+ .addRunClasspathFiles(buildMockAndroidRuntimeLibrary(androidRuntimeLibraryMock))
.run(mainClassName)
.assertSuccessWithOutput(expectedOutput);
} else {
assert backend == Backend.CF;
+ Path androidRuntimeLibraryMock = mockAndroidRuntimeLibrary(AndroidApiLevel.D);
testForR8(backend)
.addProgramFiles(buildApp(nativeApiLevel))
.addKeepMainRule(mainClassName)
.addKeepRules(additionalKeepRules)
+ .addDontWarn("android.os.Build$VERSION", "android.os.Native")
.apply(configuration)
.compile()
.inspectSyntheticProguardRules(this::noSynthesizedRules)
- .addRunClasspathFiles(
- ImmutableList.of(mockAndroidRuntimeLibrary(AndroidApiLevel.D.getLevel())))
+ .addRunClasspathFiles(androidRuntimeLibraryMock)
.run(mainClassName)
.assertSuccessWithOutput(expectedResultForCompat(AndroidApiLevel.D));
}
diff --git a/src/test/java/com/android/tools/r8/shaking/attributes/KeepAttributesDotsTest.java b/src/test/java/com/android/tools/r8/shaking/attributes/KeepAttributesDotsTest.java
index 1caf4f2..a913463 100644
--- a/src/test/java/com/android/tools/r8/shaking/attributes/KeepAttributesDotsTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/attributes/KeepAttributesDotsTest.java
@@ -47,10 +47,10 @@
@Test
public void testProguard() throws ExecutionException, CompilationFailedException, IOException {
testForProguard()
- .addProgramClassesAndInnerClasses(Main.class)
+ .addInnerClasses(getClass())
.addKeepAllClassesRule()
.addKeepAttributes(keepAttributes)
- .addKeepRules("-dontwarn com.android.tools.r8.shaking.attributes.*")
+ .addDontWarn(KeepAttributesDotsTest.class)
.run(TestRuntime.getCheckedInJdk9(), Main.class)
.assertSuccessWithOutputLines("Hello World!")
.inspect(this::inspect);
@@ -59,7 +59,7 @@
@Test
public void testR8() throws IOException, CompilationFailedException, ExecutionException {
testForR8(Backend.CF)
- .addProgramClassesAndInnerClasses(Main.class)
+ .addInnerClasses(getClass())
.addKeepAttributes(keepAttributes)
.addKeepAllClassesRule()
.run(TestRuntime.getCheckedInJdk9(), Main.class)
diff --git a/src/test/java/com/android/tools/r8/shaking/attributes/KeepAttributesTest.java b/src/test/java/com/android/tools/r8/shaking/attributes/KeepAttributesTest.java
index f298046..a314e96 100644
--- a/src/test/java/com/android/tools/r8/shaking/attributes/KeepAttributesTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/attributes/KeepAttributesTest.java
@@ -27,11 +27,11 @@
@RunWith(Parameterized.class)
public class KeepAttributesTest extends TestBase {
- private static final Class CLASS = TestKeepAttributes.class;
+ private static final Class<?> CLASS = TestKeepAttributes.class;
@Parameters(name = "{0}")
public static TestParametersCollection parameters() {
- return getTestParameters().withAllRuntimes().build();
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
}
private final TestParameters parameters;
@@ -111,7 +111,8 @@
.addProgramClassesAndInnerClasses(CLASS)
.addKeepAllClassesRule()
.addKeepRules(keepRules)
- .setMinApi(parameters.getRuntime())
+ .enableSideEffectAnnotations()
+ .setMinApi(parameters.getApiLevel())
.run(parameters.getRuntime(), CLASS)
.inspector()
.clazz(CLASS)
diff --git a/src/test/java/com/android/tools/r8/shaking/b134858535/EventEntity.java b/src/test/java/com/android/tools/r8/shaking/b134858535/EventEntity.java
new file mode 100644
index 0000000..0f92507
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/shaking/b134858535/EventEntity.java
@@ -0,0 +1,7 @@
+// 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.shaking.b134858535;
+
+public class EventEntity {}
diff --git a/src/test/java/com/android/tools/r8/shaking/b134858535/EventPublisher$bDump.java b/src/test/java/com/android/tools/r8/shaking/b134858535/EventPublisher$bDump.java
index df725af..4836ae1 100644
--- a/src/test/java/com/android/tools/r8/shaking/b134858535/EventPublisher$bDump.java
+++ b/src/test/java/com/android/tools/r8/shaking/b134858535/EventPublisher$bDump.java
@@ -68,29 +68,33 @@
{
AnnotationVisitor annotationVisitor1 = annotationVisitor0.visitArray("d2");
annotationVisitor1.visit(null, "<anonymous>");
- annotationVisitor1.visit(null, "Lio/reactivex/Flowable;");
+ annotationVisitor1.visit(null, "Lcom/android/tools/r8/shaking/b134858535/Flowable;");
annotationVisitor1.visit(null, "Lkotlin/Pair;");
- annotationVisitor1.visit(null, "Lcom/permutive/android/event/db/model/EventEntity;");
+ annotationVisitor1.visit(null, "Lcom/android/tools/r8/shaking/b134858535/EventEntity;");
annotationVisitor1.visit(null, "kotlin.jvm.PlatformType");
annotationVisitor1.visit(
- null, "Lcom/permutive/android/event/api/model/TrackBatchEventResponse;");
+ null, "Lcom/android/tools/r8/shaking/b134858535/TrackBatchEventResponse;");
annotationVisitor1.visit(null, "<name for destructuring parameter 0>");
annotationVisitor1.visit(null, "");
- annotationVisitor1.visit(null, "Lcom/permutive/android/config/api/model/SdkConfiguration;");
+ annotationVisitor1.visit(
+ null, "Lcom/android/tools/r8/shaking/b134858535/SdkConfiguration;");
annotationVisitor1.visit(null, "apply");
annotationVisitor1.visitEnd();
}
annotationVisitor0.visitEnd();
}
classWriter.visitInnerClass(
- "com/permutive/android/event/EventPublisher$b", null, null, ACC_FINAL | ACC_STATIC);
+ "com/android/tools/r8/shaking/b134858535/EventPublisher$b",
+ null,
+ null,
+ ACC_FINAL | ACC_STATIC);
{
fieldVisitor =
classWriter.visitField(
ACC_FINAL | ACC_SYNTHETIC,
"a",
- "Lcom/permutive/android/event/EventPublisher;",
+ "Lcom/android/tools/r8/shaking/b134858535/EventPublisher;",
null,
null);
fieldVisitor.visitEnd();
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 8bde97b..6f9c298 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
@@ -4,32 +4,62 @@
package com.android.tools.r8.shaking.b134858535;
+import static com.android.tools.r8.ToolHelper.getKotlinCompilers;
import com.android.tools.r8.CompilationFailedException;
+import com.android.tools.r8.KotlinCompilerTool.KotlinCompiler;
import com.android.tools.r8.TestBase;
-import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.ToolHelper;
+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 EventPublisherTest extends TestBase {
+ private final KotlinCompiler kotlinc;
+ private final TestParameters parameters;
+
+ @Parameters(name = "{1}, {0}")
+ public static List<Object[]> data() {
+ return buildParameters(
+ getKotlinCompilers(),
+ TestBase.getTestParameters().withDexRuntimes().withAllApiLevels().build());
+ }
+
+ public EventPublisherTest(KotlinCompiler kotlinc, TestParameters parameters) {
+ this.kotlinc = kotlinc;
+ this.parameters = parameters;
+ }
+
+ @Test
+ public void testPrivateMethodsInLambdaClass() throws CompilationFailedException {
+ // This test only tests if the dump can be compiled without errors.
+ testForR8(parameters.getBackend())
+ .addProgramClasses(
+ Main.class,
+ Interface.class,
+ EventEntity.class,
+ Flowable.class,
+ SdkConfiguration.class,
+ TrackBatchEventResponse.class)
+ .addProgramClassFileData(EventPublisher$bDump.dump())
+ .addProgramFiles(ToolHelper.getKotlinStdlibJar(kotlinc))
+ .addKeepClassRules(Interface.class)
+ .addKeepMainRule(Main.class)
+ .setMinApi(parameters.getApiLevel())
+ .addHorizontallyMergedLambdaClassesInspector(
+ inspector -> inspector.assertClassNotMerged(EventPublisher$b.class))
+ .compile();
+ }
+
public static class Main {
public static void main(String[] args) {
new EventPublisher$b().apply("foo");
}
}
-
- @Test
- public void testPrivateMethodsInLambdaClass() throws CompilationFailedException {
- // This test only tests if the dump can be compiled without errors.
- testForR8(Backend.DEX)
- .addProgramClasses(Main.class, Interface.class)
- .addProgramClassFileData(EventPublisher$bDump.dump())
- .addKeepClassRules(Interface.class)
- .addKeepMainRule(Main.class)
- .setMinApi(AndroidApiLevel.L)
- .addHorizontallyMergedLambdaClassesInspector(
- inspector -> inspector.assertClassNotMerged(EventPublisher$b.class))
- .compile();
- }
}
diff --git a/src/test/java/com/android/tools/r8/shaking/b134858535/Flowable.java b/src/test/java/com/android/tools/r8/shaking/b134858535/Flowable.java
new file mode 100644
index 0000000..928744f
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/shaking/b134858535/Flowable.java
@@ -0,0 +1,7 @@
+// 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.shaking.b134858535;
+
+public class Flowable {}
diff --git a/src/test/java/com/android/tools/r8/shaking/b134858535/SdkConfiguration.java b/src/test/java/com/android/tools/r8/shaking/b134858535/SdkConfiguration.java
new file mode 100644
index 0000000..39f22e3
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/shaking/b134858535/SdkConfiguration.java
@@ -0,0 +1,7 @@
+// 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.shaking.b134858535;
+
+public class SdkConfiguration {}
diff --git a/src/test/java/com/android/tools/r8/shaking/b134858535/TrackBatchEventResponse.java b/src/test/java/com/android/tools/r8/shaking/b134858535/TrackBatchEventResponse.java
new file mode 100644
index 0000000..ae8dfb6
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/shaking/b134858535/TrackBatchEventResponse.java
@@ -0,0 +1,7 @@
+// 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.shaking.b134858535;
+
+public class TrackBatchEventResponse {}
diff --git a/src/test/java/com/android/tools/r8/shaking/clinit/ClassNotInitializedByCheckCastTest.java b/src/test/java/com/android/tools/r8/shaking/clinit/ClassNotInitializedByCheckCastTest.java
index 5a3efa6..484526f 100644
--- a/src/test/java/com/android/tools/r8/shaking/clinit/ClassNotInitializedByCheckCastTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/clinit/ClassNotInitializedByCheckCastTest.java
@@ -5,13 +5,11 @@
package com.android.tools.r8.shaking.clinit;
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 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 com.android.tools.r8.utils.codeinspector.CodeInspector;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -45,15 +43,8 @@
}
private void inspect(CodeInspector inspector) {
- if (parameters.isCfRuntime()) {
- // Check that A.<clinit>() is removed.
- ClassSubject aClassSubject = inspector.clazz(A.class);
- assertThat(aClassSubject, isPresent());
- assertThat(aClassSubject.clinit(), isAbsent());
- } else {
- // Check that A is removed.
- assertThat(inspector.clazz(A.class), isAbsent());
- }
+ // Check that A is removed.
+ assertThat(inspector.clazz(A.class), isAbsent());
}
static class TestClass {
diff --git a/src/test/java/com/android/tools/r8/shaking/defaultmethods/DefaultMethodsTest.java b/src/test/java/com/android/tools/r8/shaking/defaultmethods/DefaultMethodsTest.java
index 32a861a..0a54cf8 100644
--- a/src/test/java/com/android/tools/r8/shaking/defaultmethods/DefaultMethodsTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/defaultmethods/DefaultMethodsTest.java
@@ -44,7 +44,10 @@
throws Exception {
testForR8(parameters.getBackend())
.addProgramClasses(
- InterfaceWithDefaultMethods.class, ClassImplementingInterface.class, TestClass.class)
+ InterfaceWithDefaultMethods.class,
+ ClassImplementingInterface.class,
+ OtherClassImplementingInterface.class,
+ TestClass.class)
.setMinApi(parameters.getApiLevel())
.addKeepMainRule(TestClass.class)
.addKeepRules(additionalKeepRules)
diff --git a/src/test/java/com/android/tools/r8/shaking/examples/TreeShaking12Test.java b/src/test/java/com/android/tools/r8/shaking/examples/TreeShaking12Test.java
index b30a14d..15e18bd 100644
--- a/src/test/java/com/android/tools/r8/shaking/examples/TreeShaking12Test.java
+++ b/src/test/java/com/android/tools/r8/shaking/examples/TreeShaking12Test.java
@@ -9,6 +9,7 @@
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.shaking.TreeShakingTest;
+import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.codeinspector.ClassSubject;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
import com.google.common.collect.ImmutableList;
@@ -25,7 +26,13 @@
@Parameters(name = "mode:{0}-{1} minify:{2}")
public static List<Object[]> data() {
- return defaultTreeShakingParameters();
+ return buildParameters(
+ Frontend.values(),
+ getTestParameters()
+ .withAllRuntimes()
+ .withApiLevelsStartingAtIncluding(AndroidApiLevel.K)
+ .build(),
+ MinifyMode.values());
}
public TreeShaking12Test(Frontend frontend, TestParameters parameters, MinifyMode minify) {
diff --git a/src/test/java/com/android/tools/r8/shaking/forceproguardcompatibility/ForceProguardCompatibilityTest.java b/src/test/java/com/android/tools/r8/shaking/forceproguardcompatibility/ForceProguardCompatibilityTest.java
index 04e2c9f..4e53505 100644
--- a/src/test/java/com/android/tools/r8/shaking/forceproguardcompatibility/ForceProguardCompatibilityTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/forceproguardcompatibility/ForceProguardCompatibilityTest.java
@@ -14,9 +14,11 @@
import static org.junit.Assume.assumeTrue;
import com.android.tools.r8.CompilationFailedException;
+import com.android.tools.r8.R8CompatTestBuilder;
import com.android.tools.r8.R8TestCompileResult;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.ThrowableConsumer;
import com.android.tools.r8.ToolHelper;
import com.android.tools.r8.naming.MemberNaming.MethodSignature;
import com.android.tools.r8.shaking.ProguardKeepAttributes;
@@ -68,12 +70,21 @@
}
private void test(Class<?> mainClass, Class<?> mentionedClass) throws Exception {
+ test(mainClass, mentionedClass, null);
+ }
+
+ private void test(
+ Class<?> mainClass,
+ Class<?> mentionedClass,
+ ThrowableConsumer<R8CompatTestBuilder> configuration)
+ throws Exception {
CodeInspector inspector =
testForR8Compat(parameters.getBackend(), forceProguardCompatibility)
.noMinification()
.allowAccessModification()
.addProgramClasses(mainClass, mentionedClass)
.addKeepMainRule(mainClass)
+ .apply(configuration)
.setMinApi(parameters.getApiLevel())
.compile()
.inspector();
@@ -86,7 +97,12 @@
@Test
public void testKeepDefaultInitializer() throws Exception {
- test(TestMain.class, TestMain.MentionedClass.class);
+ test(
+ TestMain.class,
+ TestMain.MentionedClass.class,
+ testBuilder ->
+ testBuilder.addProgramClasses(
+ TestMain.MentionedClassWithAnnotation.class, TestAnnotation.class));
}
@Test
@@ -215,7 +231,7 @@
testCheckCast(
TestMainWithCheckCast.class,
TestClassWithDefaultConstructor.class,
- forceProguardCompatibility || parameters.isCfRuntime());
+ forceProguardCompatibility);
testCheckCast(TestMainWithoutCheckCast.class, TestClassWithDefaultConstructor.class, false);
}
diff --git a/src/test/java/com/android/tools/r8/shaking/forceproguardcompatibility/keepinterface/CompatKeepInterfaceAsInstantiatedTest.java b/src/test/java/com/android/tools/r8/shaking/forceproguardcompatibility/keepinterface/CompatKeepInterfaceAsInstantiatedTest.java
index 7f4606c..38a6b4f 100644
--- a/src/test/java/com/android/tools/r8/shaking/forceproguardcompatibility/keepinterface/CompatKeepInterfaceAsInstantiatedTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/forceproguardcompatibility/keepinterface/CompatKeepInterfaceAsInstantiatedTest.java
@@ -127,7 +127,9 @@
throws Exception {
return builder
// Add -dontwarn to avoid PG failing since this test runner class is not present.
- .addKeepRules("-dontwarn " + CompatKeepInterfaceAsInstantiatedTest.class.getTypeName())
+ .applyIf(
+ builder.isProguardTestBuilder(),
+ b -> b.addDontWarn(CompatKeepInterfaceAsInstantiatedTest.class))
.noMinification()
.addProgramClasses(main, Foo.class)
.addKeepMainRule(main)
diff --git a/src/test/java/com/android/tools/r8/shaking/ifrule/membervaluepropagation/IfWithFieldValuePropagationTest.java b/src/test/java/com/android/tools/r8/shaking/ifrule/membervaluepropagation/IfWithFieldValuePropagationTest.java
index 908297c..cc1a987 100644
--- a/src/test/java/com/android/tools/r8/shaking/ifrule/membervaluepropagation/IfWithFieldValuePropagationTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/ifrule/membervaluepropagation/IfWithFieldValuePropagationTest.java
@@ -23,7 +23,7 @@
@Parameterized.Parameters(name = "{0}")
public static TestParametersCollection data() {
- return getTestParameters().withAllRuntimes().build();
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
}
public IfWithFieldValuePropagationTest(TestParameters parameters) {
@@ -48,8 +48,9 @@
.addRunClasspathFiles(
testForR8(parameters.getBackend())
.addProgramClasses(Library.class)
+ .addClasspathClasses(Layout.class)
.addKeepAllClassesRule()
- .setMinApi(parameters.getRuntime())
+ .setMinApi(parameters.getApiLevel())
.compile()
.writeToZip())
.run(parameters.getRuntime(), TestClass.class)
diff --git a/src/test/java/com/android/tools/r8/shaking/ifrule/membervaluepropagation/IfWithMethodValuePropagationTest.java b/src/test/java/com/android/tools/r8/shaking/ifrule/membervaluepropagation/IfWithMethodValuePropagationTest.java
index f795067..e377809 100644
--- a/src/test/java/com/android/tools/r8/shaking/ifrule/membervaluepropagation/IfWithMethodValuePropagationTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/ifrule/membervaluepropagation/IfWithMethodValuePropagationTest.java
@@ -48,6 +48,7 @@
.addRunClasspathFiles(
testForR8(parameters.getBackend())
.addProgramClasses(Library.class)
+ .addClasspathClasses(Layout.class)
.addKeepAllClassesRule()
.setMinApi(parameters.getRuntime())
.compile()
diff --git a/src/test/java/com/android/tools/r8/shaking/ifrule/verticalclassmerging/MergedFieldTypeTest.java b/src/test/java/com/android/tools/r8/shaking/ifrule/verticalclassmerging/MergedFieldTypeTest.java
index 7ea419c..6b01b51 100644
--- a/src/test/java/com/android/tools/r8/shaking/ifrule/verticalclassmerging/MergedFieldTypeTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/ifrule/verticalclassmerging/MergedFieldTypeTest.java
@@ -11,6 +11,7 @@
import static org.junit.Assert.assertNotEquals;
import com.android.tools.r8.NeverClassInline;
+import com.android.tools.r8.R8FullTestBuilder;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.utils.codeinspector.ClassSubject;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
@@ -78,6 +79,11 @@
}
@Override
+ public void configure(R8FullTestBuilder builder) {
+ builder.enableNeverClassInliningAnnotations();
+ }
+
+ @Override
public Class<?> getTestClass() {
return TestClass.class;
}
diff --git a/src/test/java/com/android/tools/r8/shaking/interfaces/RedundantImplementsClauseTest.java b/src/test/java/com/android/tools/r8/shaking/interfaces/RedundantImplementsClauseTest.java
index 1d559b7..fae800c 100644
--- a/src/test/java/com/android/tools/r8/shaking/interfaces/RedundantImplementsClauseTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/interfaces/RedundantImplementsClauseTest.java
@@ -43,6 +43,7 @@
.addKeepMainRule(TestClass.class)
.enableInliningAnnotations()
.enableNeverClassInliningAnnotations()
+ .enableNoVerticalClassMergingAnnotations()
.setMinApi(parameters.getApiLevel())
.compile()
.inspect(this::inspect)
diff --git a/src/test/java/com/android/tools/r8/shaking/keepparameternames/KeepParameterNamesTest.java b/src/test/java/com/android/tools/r8/shaking/keepparameternames/KeepParameterNamesTest.java
index 888b231..4740853 100644
--- a/src/test/java/com/android/tools/r8/shaking/keepparameternames/KeepParameterNamesTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/keepparameternames/KeepParameterNamesTest.java
@@ -173,6 +173,9 @@
.addInnerClasses(KeepParameterNamesTest.class)
.addKeepMainRule(TestClass.class)
.addKeepRules("-keep class " + Api.class.getTypeName() + "{ api*(...); }")
+ .enableInliningAnnotations()
+ .enableNeverClassInliningAnnotations()
+ .enableUnusedArgumentAnnotations()
.minification(enableMinification)
.apply(this::configureKeepParameterNames)
.compile()
diff --git a/src/test/java/com/android/tools/r8/shaking/keptgraph/KeptByAnnotatedClassTestRunner.java b/src/test/java/com/android/tools/r8/shaking/keptgraph/KeptByAnnotatedClassTestRunner.java
index 47659e2..3f3c43e 100644
--- a/src/test/java/com/android/tools/r8/shaking/keptgraph/KeptByAnnotatedClassTestRunner.java
+++ b/src/test/java/com/android/tools/r8/shaking/keptgraph/KeptByAnnotatedClassTestRunner.java
@@ -46,7 +46,9 @@
.enableGraphInspector()
.enableInliningAnnotations()
.addProgramClasses(CLASS)
+ .addKeepAnnotation()
.addKeepRules("-keep @com.android.tools.r8.Keep class * { public *; }")
+ .addInliningAnnotations()
.run(CLASS)
.assertSuccessWithOutput(EXPECTED)
.graphInspector();
diff --git a/src/test/java/com/android/tools/r8/shaking/keptgraph/KeptByAnnotatedMethodTestRunner.java b/src/test/java/com/android/tools/r8/shaking/keptgraph/KeptByAnnotatedMethodTestRunner.java
index 2f3fea1..a5fb706 100644
--- a/src/test/java/com/android/tools/r8/shaking/keptgraph/KeptByAnnotatedMethodTestRunner.java
+++ b/src/test/java/com/android/tools/r8/shaking/keptgraph/KeptByAnnotatedMethodTestRunner.java
@@ -62,6 +62,7 @@
.enableGraphInspector()
.enableInliningAnnotations()
.addProgramClasses(CLASSES)
+ .addKeepAnnotation()
.addKeepMainRule(CLASS)
.addKeepRules(keepAnnotatedMethodsRule, keepClassesOfAnnotatedMethodsRule)
.run(CLASS)
diff --git a/src/test/java/com/android/tools/r8/shaking/keptgraph/KeptByConditionalRuleTestRunner.java b/src/test/java/com/android/tools/r8/shaking/keptgraph/KeptByConditionalRuleTestRunner.java
index c813307..32c8662 100644
--- a/src/test/java/com/android/tools/r8/shaking/keptgraph/KeptByConditionalRuleTestRunner.java
+++ b/src/test/java/com/android/tools/r8/shaking/keptgraph/KeptByConditionalRuleTestRunner.java
@@ -108,7 +108,7 @@
// Check baz is kept by the if rule.
QueryNode barMethodNode = inspector.method(bazMethod).assertNotRenamed();
- ifRuleInstances.assertAllMatch(n -> barMethodNode.isKeptBy(n));
+ ifRuleInstances.assertAllMatch(barMethodNode::isKeptBy);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
whyAreYouKeepingConsumer.printWhyAreYouKeeping(bazMethod, new PrintStream(baos));
diff --git a/src/test/java/com/android/tools/r8/shaking/keptgraph/KeptByFieldReflectionTestRunner.java b/src/test/java/com/android/tools/r8/shaking/keptgraph/KeptByFieldReflectionTestRunner.java
index a43e42c..02d8481 100644
--- a/src/test/java/com/android/tools/r8/shaking/keptgraph/KeptByFieldReflectionTestRunner.java
+++ b/src/test/java/com/android/tools/r8/shaking/keptgraph/KeptByFieldReflectionTestRunner.java
@@ -30,16 +30,17 @@
private static final Class<?> CLASS = KeptByFieldReflectionTest.class;
private static final Collection<Class<?>> CLASSES = Arrays.asList(CLASS);
+ private static final String TYPE_NAME = CLASS.getTypeName();
private final String EXPECTED_STDOUT = StringUtils.lines("got foo: 42");
private final String EXPECTED_WHYAREYOUKEEPING =
StringUtils.lines(
- "int com.android.tools.r8.shaking.keptgraph.KeptByFieldReflectionTest.foo",
+ "int " + TYPE_NAME + ".foo",
"|- is reflected from:",
- "| void com.android.tools.r8.shaking.keptgraph.KeptByFieldReflectionTest.main(java.lang.String[])",
+ "| void " + TYPE_NAME + ".main(java.lang.String[])",
"|- is referenced in keep rule:",
- "| -keep class com.android.tools.r8.shaking.keptgraph.KeptByFieldReflectionTest { public static void main(java.lang.String[]); }");
+ "| -keep class " + TYPE_NAME + " { public static void main(java.lang.String[]); }");
private final Backend backend;
@@ -67,6 +68,7 @@
testForR8(backend)
.enableGraphInspector(consumer)
.addProgramClasses(CLASSES)
+ .addKeepAnnotation()
.addKeepMainRule(CLASS)
.run(CLASS)
.assertSuccessWithOutput(EXPECTED_STDOUT)
diff --git a/src/test/java/com/android/tools/r8/shaking/keptgraph/KeptByMethodReflectionTestRunner.java b/src/test/java/com/android/tools/r8/shaking/keptgraph/KeptByMethodReflectionTestRunner.java
index c8afcdc..be0fcff 100644
--- a/src/test/java/com/android/tools/r8/shaking/keptgraph/KeptByMethodReflectionTestRunner.java
+++ b/src/test/java/com/android/tools/r8/shaking/keptgraph/KeptByMethodReflectionTestRunner.java
@@ -28,16 +28,17 @@
private static final Class<?> CLASS = KeptByMethodReflectionTest.class;
private static final Collection<Class<?>> CLASSES = Arrays.asList(CLASS);
+ private static final String TYPE_NAME = CLASS.getTypeName();
private final String EXPECTED_STDOUT = StringUtils.lines("called foo");
private final String EXPECTED_WHYAREYOUKEEPING =
StringUtils.lines(
- "void com.android.tools.r8.shaking.keptgraph.KeptByMethodReflectionTest.foo()",
+ "void " + TYPE_NAME + ".foo()",
"|- is reflected from:",
- "| void com.android.tools.r8.shaking.keptgraph.KeptByMethodReflectionTest.main(java.lang.String[])",
+ "| void " + TYPE_NAME + ".main(java.lang.String[])",
"|- is referenced in keep rule:",
- "| -keep class com.android.tools.r8.shaking.keptgraph.KeptByMethodReflectionTest { public static void main(java.lang.String[]); }");
+ "| -keep class " + TYPE_NAME + " { public static void main(java.lang.String[]); }");
private final Backend backend;
@@ -64,6 +65,7 @@
testForR8(backend)
.enableGraphInspector(consumer)
.addProgramClasses(CLASSES)
+ .addKeepAnnotation()
.addKeepMainRule(CLASS)
.run(CLASS)
.assertSuccessWithOutput(EXPECTED_STDOUT)
diff --git a/src/test/java/com/android/tools/r8/shaking/keptgraph/KeptByTwoRulesTestRunner.java b/src/test/java/com/android/tools/r8/shaking/keptgraph/KeptByTwoRulesTestRunner.java
index 5623924..042679f 100644
--- a/src/test/java/com/android/tools/r8/shaking/keptgraph/KeptByTwoRulesTestRunner.java
+++ b/src/test/java/com/android/tools/r8/shaking/keptgraph/KeptByTwoRulesTestRunner.java
@@ -54,6 +54,7 @@
testForR8(backend)
.enableGraphInspector()
.addProgramClasses(CLASSES)
+ .addKeepAnnotation()
.addKeepRules(keepPublicRule, keepFooRule)
.run(CLASS)
.assertSuccessWithOutput(EXPECTED)
diff --git a/src/test/java/com/android/tools/r8/shaking/keptgraph/KeptViaClassInitializerTestRunner.java b/src/test/java/com/android/tools/r8/shaking/keptgraph/KeptViaClassInitializerTestRunner.java
index 4d385a0..230c0cf 100644
--- a/src/test/java/com/android/tools/r8/shaking/keptgraph/KeptViaClassInitializerTestRunner.java
+++ b/src/test/java/com/android/tools/r8/shaking/keptgraph/KeptViaClassInitializerTestRunner.java
@@ -21,6 +21,7 @@
import com.android.tools.r8.origin.Origin;
import com.android.tools.r8.references.MethodReference;
import com.android.tools.r8.shaking.WhyAreYouKeepingConsumer;
+import com.android.tools.r8.synthesis.SyntheticItemsTestUtils;
import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.StringUtils;
import com.android.tools.r8.utils.graphinspector.GraphInspector;
@@ -28,6 +29,7 @@
import java.io.ByteArrayOutputStream;
import java.io.PrintStream;
import java.util.function.Supplier;
+import org.hamcrest.Matcher;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
@@ -110,6 +112,7 @@
.addProgramClassesAndInnerClasses(Main.class, A.class, T.class)
.addKeepMethodRules(mainMethod)
.enableMemberValuePropagationAnnotations()
+ .enableNeverClassInliningAnnotations()
.setMinApi(AndroidApiLevel.N)
.apply(
b -> {
@@ -129,12 +132,13 @@
consumer.printWhyAreYouKeeping(classFromClass(A.class), new PrintStream(baos));
assertThat(baos.toString(), containsString(KEPT_REASON_SUFFIX));
- // TODO(b/124499108): Currently synthetic lambda classes are referenced,
+ // TODO(b/124499108): Currently (internal) synthetic lambda classes are referenced,
// should be their originating context.
+ Matcher<String> hasLambda = SyntheticItemsTestUtils.containsInternalSyntheticReference();
if (parameters.isDexRuntime()) {
- assertThat(baos.toString(), containsString("-$$Lambda$"));
+ assertThat(baos.toString(), hasLambda);
} else {
- assertThat(baos.toString(), not(containsString("-$$Lambda$")));
+ assertThat(baos.toString(), not(hasLambda));
}
}
}
diff --git a/src/test/java/com/android/tools/r8/shaking/proxy/MockitoTest.java b/src/test/java/com/android/tools/r8/shaking/proxy/MockitoTest.java
index cea851e..6f868fc 100644
--- a/src/test/java/com/android/tools/r8/shaking/proxy/MockitoTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/proxy/MockitoTest.java
@@ -5,14 +5,12 @@
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;
import static org.hamcrest.CoreMatchers.not;
import static org.hamcrest.MatcherAssert.assertThat;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.ToolHelper;
-import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.BooleanUtils;
import com.android.tools.r8.utils.FileUtils;
import com.android.tools.r8.utils.codeinspector.ClassSubject;
@@ -53,13 +51,10 @@
testForR8(parameters.getBackend())
.addProgramFiles(MOCKITO_INTERFACE_JAR)
.addKeepRuleFiles(flagToKeepTestRunner)
- .allowDiagnosticWarningMessages(
- parameters.isDexRuntime() && parameters.getApiLevel().isLessThan(AndroidApiLevel.N))
+ .addDontWarn("org.junit.**", "org.mockito.**")
.minification(minify)
.setMinApi(parameters.getApiLevel())
.compile()
- .assertAllWarningMessagesMatch(
- containsString("required for default or static interface methods desugaring"))
.inspector();
ClassSubject itf = inspector.clazz(M_I);
assertThat(itf, isPresent());
@@ -75,13 +70,10 @@
testForR8(parameters.getBackend())
.addProgramFiles(MOCKITO_INTERFACE_JAR)
.addKeepRuleFiles(flagToKeepInterfaceConditionally)
- .allowDiagnosticWarningMessages(
- parameters.isDexRuntime() && parameters.getApiLevel().isLessThan(AndroidApiLevel.N))
+ .addDontWarn("org.junit.**", "org.mockito.**")
.minification(minify)
.setMinApi(parameters.getApiLevel())
.compile()
- .assertAllWarningMessagesMatch(
- containsString("required for default or static interface methods desugaring"))
.inspector();
ClassSubject itf = inspector.clazz(M_I);
assertThat(itf, isPresent());
diff --git a/src/test/java/com/android/tools/r8/shaking/testrules/C.java b/src/test/java/com/android/tools/r8/shaking/testrules/C.java
index f402864..497c218 100644
--- a/src/test/java/com/android/tools/r8/shaking/testrules/C.java
+++ b/src/test/java/com/android/tools/r8/shaking/testrules/C.java
@@ -4,14 +4,12 @@
package com.android.tools.r8.shaking.testrules;
-import com.android.tools.r8.AssumeMayHaveSideEffects;
import com.android.tools.r8.NeverInline;
public class C {
private static int i;
- @AssumeMayHaveSideEffects
@NeverInline
public static int x() {
return i;
diff --git a/src/test/java/com/android/tools/r8/shaking/testrules/ForceInlineTest.java b/src/test/java/com/android/tools/r8/shaking/testrules/ForceInlineTest.java
index d4e5972..b669c95 100644
--- a/src/test/java/com/android/tools/r8/shaking/testrules/ForceInlineTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/testrules/ForceInlineTest.java
@@ -42,6 +42,7 @@
return testForR8(parameters.getBackend())
.addProgramClasses(Main.class, A.class, B.class, C.class)
.addKeepRules(proguardConfiguration)
+ .enableInliningAnnotations()
.enableNoHorizontalClassMergingAnnotations()
.enableNoStaticClassMergingAnnotations()
.enableProguardTestOptions()
diff --git a/src/test/java/com/android/tools/r8/shaking/whyareyoukeeping/WhyAreYouKeepingTest.java b/src/test/java/com/android/tools/r8/shaking/whyareyoukeeping/WhyAreYouKeepingTest.java
index eeb280b..477e1f3 100644
--- a/src/test/java/com/android/tools/r8/shaking/whyareyoukeeping/WhyAreYouKeepingTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/whyareyoukeeping/WhyAreYouKeepingTest.java
@@ -79,6 +79,7 @@
.addKeepRules("-whyareyoukeeping class " + A.class.getTypeName())
// Redirect the compilers stdout to intercept the '-whyareyoukeeping' output
.collectStdout()
+ .addInliningAnnotations()
.compile()
.assertStdoutThatMatches(equalTo(expected));
}
@@ -90,6 +91,7 @@
.addProgramClasses(A.class)
.addKeepMethodRules(Reference.methodFromMethod(A.class.getMethod("foo")))
.setKeptGraphConsumer(graphConsumer)
+ .addInliningAnnotations()
.compile();
ByteArrayOutputStream baos = new ByteArrayOutputStream();
@@ -139,13 +141,13 @@
.allowUnusedProguardConfigurationRules()
// Redirect the compilers stdout to intercept the '-whyareyoukeeping' output
.collectStdout()
+ .addInliningAnnotations()
.compile()
.assertNoStdout();
}
@Test
public void testNonExistentMethodWhyAreYouKeepingViaProguardConfig() throws Exception {
- ByteArrayOutputStream baos = new ByteArrayOutputStream();
String aName = A.class.getTypeName();
testForR8(backend)
.addProgramClasses(A.class)
@@ -153,6 +155,7 @@
.addKeepRules("-whyareyoukeeping class " + aName + " { nonExistentMethod(); }")
// Redirect the compilers stdout to intercept the '-whyareyoukeeping' output
.collectStdout()
+ .addInliningAnnotations()
.compile()
.assertStdoutThatMatches(not(equalTo("")));
}
diff --git a/src/test/java/com/android/tools/r8/smali/ConstantFoldingTest.java b/src/test/java/com/android/tools/r8/smali/ConstantFoldingTest.java
index fb0785e..e5a8027 100644
--- a/src/test/java/com/android/tools/r8/smali/ConstantFoldingTest.java
+++ b/src/test/java/com/android/tools/r8/smali/ConstantFoldingTest.java
@@ -31,9 +31,8 @@
public class ConstantFoldingTest extends SmaliTestBase {
- @FunctionalInterface
public interface TriConsumer<T, U, V> {
- public void accept(T t, U u, V v);
+ void accept(T t, U u, V v);
}
private class SmaliBuilderWithCheckers {
diff --git a/src/test/java/com/android/tools/r8/synthesis/SyntheticItemsTestUtils.java b/src/test/java/com/android/tools/r8/synthesis/SyntheticItemsTestUtils.java
new file mode 100644
index 0000000..438d3f9
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/synthesis/SyntheticItemsTestUtils.java
@@ -0,0 +1,77 @@
+// 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.synthesis;
+
+import static org.hamcrest.CoreMatchers.containsString;
+
+import com.android.tools.r8.ir.desugar.InterfaceMethodRewriter;
+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.synthesis.SyntheticNaming.Phase;
+import com.android.tools.r8.synthesis.SyntheticNaming.SyntheticKind;
+import java.lang.reflect.Method;
+import org.hamcrest.Matcher;
+
+public class SyntheticItemsTestUtils {
+
+ public static ClassReference syntheticCompanionClass(Class<?> clazz) {
+ return Reference.classFromDescriptor(
+ InterfaceMethodRewriter.getCompanionClassDescriptor(
+ Reference.classFromClass(clazz).getDescriptor()));
+ }
+
+ private static ClassReference syntheticClass(Class<?> clazz, SyntheticKind kind, int id) {
+ return SyntheticNaming.makeSyntheticReferenceForTest(
+ Reference.classFromClass(clazz), kind, "" + id);
+ }
+
+ public static MethodReference syntheticBackportMethod(Class<?> clazz, int id, Method method) {
+ ClassReference syntheticHolder =
+ syntheticClass(clazz, SyntheticNaming.SyntheticKind.BACKPORT, id);
+ MethodReference originalMethod = Reference.methodFromMethod(method);
+ return Reference.methodFromDescriptor(
+ syntheticHolder.getDescriptor(),
+ SyntheticNaming.INTERNAL_SYNTHETIC_METHOD_PREFIX,
+ originalMethod.getMethodDescriptor());
+ }
+
+ public static ClassReference syntheticLambdaClass(Class<?> clazz, int id) {
+ return syntheticClass(clazz, SyntheticNaming.SyntheticKind.LAMBDA, id);
+ }
+
+ public static MethodReference syntheticLambdaMethod(Class<?> clazz, int id, Method method) {
+ ClassReference syntheticHolder = syntheticLambdaClass(clazz, id);
+ MethodReference originalMethod = Reference.methodFromMethod(method);
+ return Reference.methodFromDescriptor(
+ syntheticHolder.getDescriptor(),
+ originalMethod.getMethodName(),
+ originalMethod.getMethodDescriptor());
+ }
+
+ public static boolean isInternalLambda(ClassReference reference) {
+ return SyntheticNaming.isSynthetic(reference, Phase.INTERNAL, SyntheticKind.LAMBDA);
+ }
+
+ public static boolean isExternalLambda(ClassReference reference) {
+ return SyntheticNaming.isSynthetic(reference, Phase.EXTERNAL, SyntheticKind.LAMBDA);
+ }
+
+ public static boolean isExternalStaticInterfaceCall(ClassReference reference) {
+ return SyntheticNaming.isSynthetic(
+ reference, Phase.EXTERNAL, SyntheticKind.STATIC_INTERFACE_CALL);
+ }
+
+ public static boolean isExternalTwrCloseMethod(ClassReference reference) {
+ return SyntheticNaming.isSynthetic(reference, Phase.EXTERNAL, SyntheticKind.TWR_CLOSE_RESOURCE);
+ }
+
+ public static Matcher<String> containsInternalSyntheticReference() {
+ return containsString(SyntheticNaming.getPhaseSeparator(Phase.INTERNAL));
+ }
+
+ public static Matcher<String> containsExternalSyntheticReference() {
+ return containsString(SyntheticNaming.getPhaseSeparator(Phase.EXTERNAL));
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/utils/StringUtilsTest.java b/src/test/java/com/android/tools/r8/utils/StringUtilsTest.java
index cac07ed..8279af3 100644
--- a/src/test/java/com/android/tools/r8/utils/StringUtilsTest.java
+++ b/src/test/java/com/android/tools/r8/utils/StringUtilsTest.java
@@ -33,9 +33,9 @@
private void assertListEquals(List<String> xs, List<String> ys) {
assertEquals(
- StringUtils.join(xs, ", ", BraceType.SQUARE, s -> '"' + StringUtils.toASCIIString(s) + '"'),
- StringUtils.join(ys, ", ", BraceType.SQUARE, s -> '"' + StringUtils.toASCIIString(s) + '"')
- );
+ StringUtils.join(", ", xs, s -> '"' + StringUtils.toASCIIString(s) + '"', BraceType.SQUARE),
+ StringUtils.join(
+ ", ", ys, s -> '"' + StringUtils.toASCIIString(s) + '"', BraceType.SQUARE));
}
@Test
diff --git a/src/test/java/com/android/tools/r8/utils/SyntheticItemsTestUtils.java b/src/test/java/com/android/tools/r8/utils/SyntheticItemsTestUtils.java
deleted file mode 100644
index 9e78bac..0000000
--- a/src/test/java/com/android/tools/r8/utils/SyntheticItemsTestUtils.java
+++ /dev/null
@@ -1,27 +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;
-
-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.synthesis.SyntheticItems;
-import java.lang.reflect.Method;
-
-public class SyntheticItemsTestUtils {
-
- public static ClassReference syntheticClass(Class<?> clazz, int id) {
- return Reference.classFromTypeName(
- clazz.getTypeName() + SyntheticItems.EXTERNAL_SYNTHETIC_CLASS_SEPARATOR + id);
- }
-
- public static MethodReference syntheticMethod(Class<?> clazz, int id, Method method) {
- ClassReference syntheticHolder = syntheticClass(clazz, id);
- MethodReference originalMethod = Reference.methodFromMethod(method);
- return Reference.methodFromDescriptor(
- syntheticHolder.getDescriptor(),
- SyntheticItems.INTERNAL_SYNTHETIC_METHOD_PREFIX + 0,
- originalMethod.getMethodDescriptor());
- }
-}
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 ffe8151..a42fa4b 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
@@ -136,6 +136,11 @@
}
@Override
+ public ClassReference getOriginalReference() {
+ return null;
+ }
+
+ @Override
public ClassReference getFinalReference() {
return null;
}
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 87eb583..e9634a5 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
@@ -13,9 +13,14 @@
public class AssertUtils {
- public static <E extends Throwable> void assertFailsCompilation(ThrowingAction<E> action)
- throws E {
- assertFailsCompilationIf(true, action);
+ public static void assertFailsCompilation(ThrowingAction<CompilationFailedException> action) {
+ try {
+ assertFailsCompilationIf(true, action);
+ return;
+ } catch (CompilationFailedException e) {
+ // Should have been caught
+ }
+ fail("Should have failed with a CompilationFailedException");
}
public static <E extends Throwable> void assertFailsCompilation(
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/CfInstructionSubject.java b/src/test/java/com/android/tools/r8/utils/codeinspector/CfInstructionSubject.java
index 3a9976f..f927e62 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/CfInstructionSubject.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/CfInstructionSubject.java
@@ -285,6 +285,7 @@
&& ((CfInvoke) instruction).getOpcode() == Opcodes.INVOKESPECIAL;
}
+ @Override
public boolean isInvokeDynamic() {
return instruction instanceof CfInvokeDynamic;
}
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 a2d32eb..20d7466 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
@@ -191,6 +191,8 @@
public abstract String getOriginalBinaryName();
+ public abstract ClassReference getOriginalReference();
+
public abstract ClassReference getFinalReference();
public abstract String getFinalName();
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/DexInstructionSubject.java b/src/test/java/com/android/tools/r8/utils/codeinspector/DexInstructionSubject.java
index 4b9f733..2ae5f05 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/DexInstructionSubject.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/DexInstructionSubject.java
@@ -209,6 +209,11 @@
return false;
}
+ @Override
+ public boolean isInvokeDynamic() {
+ return isInvokeCustom();
+ }
+
public boolean isInvokeCustom() {
return instruction instanceof InvokeCustom || instruction instanceof InvokeCustomRange;
}
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/DexTryCatchSubject.java b/src/test/java/com/android/tools/r8/utils/codeinspector/DexTryCatchSubject.java
index c1fa59a..fea4fbd 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/DexTryCatchSubject.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/DexTryCatchSubject.java
@@ -54,7 +54,7 @@
@Override
public Stream<TypeSubject> streamGuards() {
return Arrays.stream(tryHandler.pairs)
- .map(pair -> pair.getType())
+ .map(TypeAddrPair::getType)
.map(type -> new TypeSubject(inspector, type));
}
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 a676666..4220b50 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
@@ -34,6 +34,7 @@
import com.android.tools.r8.retrace.RetraceTypeResult;
import com.android.tools.r8.retrace.RetracedField;
import com.android.tools.r8.retrace.Retracer;
+import com.android.tools.r8.synthesis.SyntheticItemsTestUtils;
import com.android.tools.r8.utils.DescriptorUtils;
import com.android.tools.r8.utils.StringUtils;
import com.android.tools.r8.utils.ZipUtils;
@@ -389,6 +390,11 @@
}
@Override
+ public ClassReference getOriginalReference() {
+ return Reference.classFromDescriptor(getOriginalDescriptor());
+ }
+
+ @Override
public ClassReference getFinalReference() {
return Reference.classFromDescriptor(getFinalDescriptor());
}
@@ -430,7 +436,9 @@
@Override
public boolean isSynthesizedJavaLambdaClass() {
- return dexClass.type.getName().contains("$Lambda$");
+ // TODO(141287349): Make this precise based on the map input.
+ return SyntheticItemsTestUtils.isExternalLambda(getOriginalReference())
+ || SyntheticItemsTestUtils.isExternalLambda(getFinalReference());
}
@Override
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/InstructionSubject.java b/src/test/java/com/android/tools/r8/utils/codeinspector/InstructionSubject.java
index 56436e9..8b34260 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/InstructionSubject.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/InstructionSubject.java
@@ -43,6 +43,8 @@
boolean isInvokeSpecial();
+ boolean isInvokeDynamic();
+
DexMethod getMethod();
boolean isNop();
diff --git a/tools/apk_utils.py b/tools/apk_utils.py
old mode 100644
new mode 100755
index 9155657..c3c616b
--- a/tools/apk_utils.py
+++ b/tools/apk_utils.py
@@ -1,12 +1,40 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
# 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.
+import optparse
import os
import subprocess
+import sys
import utils
+USAGE = 'usage: %prog [options] <apk>'
+
+def parse_options():
+ parser = optparse.OptionParser(usage=USAGE)
+ parser.add_option('--keystore',
+ help='keystore file (default ~/.android/app.keystore)',
+ default='~/.android/app.keystore')
+ parser.add_option('--sign',
+ help='Sign the passed in apk.',
+ default=False,
+ action='store_true')
+ parser.add_option('--use_apksigner',
+ help='Use apksigner to sign.',
+ default=False,
+ action='store_true')
+ parser.add_option('--output',
+ help='Where to put the signed apk.)',
+ default=None)
+
+ (options, args) = parser.parse_args()
+ if len(args) != 1:
+ parser.error('Expected <apk> argument, got: ' + ' '.join(args))
+ apk = args[0]
+ return (options, apk)
+
+
def sign(unsigned_apk, signed_apk, keystore, quiet=False, logging=True):
utils.Print('Signing (ignore the warnings)', quiet=quiet)
cmd = ['zip', '-d', unsigned_apk, 'META-INF/*']
@@ -37,3 +65,22 @@
unsigned_apk
]
utils.RunCmd(cmd, quiet=quiet, logging=logging)
+
+
+def main():
+ (options, apk) = parse_options()
+ if options.sign:
+ if not options.output:
+ print('When signing you must specify an output apk')
+ return 1
+ if not options.keystore:
+ print('When signing you must specify a keystore')
+ return 1
+ if options.use_apksigner:
+ sign_with_apksigner(apk, options.output, options.keystore)
+ else:
+ sign(apk, options.output, options.keystore)
+ return 0
+
+if __name__ == '__main__':
+ sys.exit(main())
diff --git a/tools/r8_release.py b/tools/r8_release.py
index 31bb4c6..179a50f 100755
--- a/tools/r8_release.py
+++ b/tools/r8_release.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
# 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.
@@ -41,7 +41,7 @@
def prepare_release(args):
if args.version:
- print "Cannot manually specify version when making a dev release."
+ print("Cannot manually specify version when making a dev release.")
sys.exit(1)
def make_release(args):
@@ -57,13 +57,13 @@
if result:
break
if not result or not result.group(1):
- print 'Failed to find version label matching %s(\d+)-dev'\
- % R8_DEV_BRANCH
+ print('Failed to find version label matching %s(\d+)-dev'\
+ % R8_DEV_BRANCH)
sys.exit(1)
try:
patch_version = int(result.group(1))
except ValueError:
- print 'Failed to convert version to integer: %s' % result.group(1)
+ print('Failed to convert version to integer: %s' % result.group(1))
old_version = '%s.%s-dev' % (R8_DEV_BRANCH, patch_version)
version = '%s.%s-dev' % (R8_DEV_BRANCH, patch_version + 1)
@@ -74,8 +74,8 @@
other_diff = version_change_diff(
merge_diff_output, old_version, "master")
if not other_diff:
- print 'Merge point from master (%s)' % commithash, \
- 'is the same as exiting release (%s).' % old_version
+ print('Merge point from master (%s)' % commithash, \
+ 'is the same as exiting release (%s).' % old_version)
sys.exit(1)
if args.dev_pre_cherry_pick:
@@ -102,7 +102,7 @@
if not args.dry_run:
input = raw_input('Publish dev release version %s [y/N]:' % version)
if input != 'y':
- print 'Aborting dev release for %s' % version
+ print('Aborting dev release for %s' % version)
sys.exit(1)
maybe_check_call(args, [
@@ -139,25 +139,25 @@
def validate_version_change_diff(version_diff_output, old_version, new_version):
invalid = version_change_diff(version_diff_output, old_version, new_version)
if invalid:
- print "Unexpected diff:"
- print "=" * 80
- print version_diff_output
- print "=" * 80
+ print("Unexpected diff:")
+ print("=" * 80)
+ print(version_diff_output)
+ print("=" * 80)
accept_string = 'THE DIFF IS OK!'
input = raw_input(
"Accept the additonal diff as part of the release? "
"Type '%s' to accept: " % accept_string)
if input != accept_string:
- print "You did not type '%s'" % accept_string
- print 'Aborting dev release for %s' % version
+ print("You did not type '%s'" % accept_string)
+ print('Aborting dev release for %s' % version)
sys.exit(1)
def maybe_check_call(args, cmd):
if args.dry_run:
- print 'DryRun:', ' '.join(cmd)
+ print('DryRun:', ' '.join(cmd))
else:
- print ' '.join(cmd)
+ print(' '.join(cmd))
return subprocess.check_call(cmd)
@@ -185,7 +185,7 @@
if not options.use_existing_work_branch:
subprocess.check_call(['git', 'commit', '-a', '-m', git_message])
else:
- print ('Not committing when --use-existing-work-branch. '
+ print('Not committing when --use-existing-work-branch. '
+ 'Commit message should be:\n\n'
+ git_message
+ '\n')
@@ -194,7 +194,7 @@
if not options.no_upload and not options.use_existing_work_branch:
process = subprocess.Popen(['repo', 'upload', '.', '--verify'],
stdin=subprocess.PIPE)
- return process.communicate(input='y\n')[0]
+ return process.communicate(input=b'y\n')[0]
def prepare_aosp(args):
@@ -202,7 +202,7 @@
assert os.path.exists(args.aosp), "Could not find AOSP path %s" % args.aosp
def release_aosp(options):
- print "Releasing for AOSP"
+ print("Releasing for AOSP")
if options.dry_run:
return 'DryRun: omitting AOSP release for %s' % options.version
@@ -228,7 +228,7 @@
gfile = '/bigstore/r8-releases/raw/%s/r8lib.zip' % args.version
release_id = gmaven_publisher_stage(options, [gfile])
- print "Staged Release ID " + release_id + ".\n"
+ print("Staged Release ID " + release_id + ".\n")
gmaven_publisher_stage_redir_test_info(
release_id, "com.android.tools:r8:%s" % args.version, "r8lib.jar")
@@ -236,13 +236,13 @@
input = raw_input("Continue with publishing [y/N]:")
if input != 'y':
- print 'Aborting release to Google maven'
+ print('Aborting release to Google maven')
sys.exit(1)
gmaven_publisher_publish(args, release_id)
- print
- print "Published. Use the email workflow for approval."
+ print("")
+ print("Published. Use the email workflow for approval.")
return release_maven
@@ -275,7 +275,7 @@
% args.studio)
def release_studio(options):
- print "Releasing for STUDIO"
+ print("Releasing for STUDIO")
if options.dry_run:
return 'DryRun: omitting studio release for %s' % options.version
@@ -329,7 +329,7 @@
def download_gfile(gfile, dst):
if not gfile.startswith('/bigstore/r8-releases'):
- print 'Unexpected gfile prefix for %s' % gfile
+ print('Unexpected gfile prefix for %s' % gfile)
sys.exit(1)
urllib.urlretrieve(
@@ -348,7 +348,7 @@
check_no_google3_client(args, args.p4_client)
def release_google3(options):
- print "Releasing for Google 3"
+ print("Releasing for Google 3")
if options.dry_run:
return 'DryRun: omitting g3 release for %s' % options.version
@@ -362,7 +362,7 @@
g4_open('src.jar')
g4_open('lib.jar')
g4_open('lib.jar.map')
- g4_open('retrace.jar')
+ g4_open('retrace_lib.jar')
g4_open('desugar_jdk_libs_configuration.jar')
download_file(options.version, 'r8-full-exclude-deps.jar', 'full.jar')
download_file(options.version, 'r8-src.jar', 'src.jar')
@@ -371,7 +371,7 @@
options.version, 'r8lib-exclude-deps.jar.map', 'lib.jar.map')
download_file(options.version, 'desugar_jdk_libs_configuration.jar',
'desugar_jdk_libs_configuration.jar')
- download_file(options.version, 'r8retrace-exclude-deps.jar', 'retrace.jar')
+ download_file(options.version, 'r8retrace-exclude-deps.jar', 'retrace_lib.jar')
g4_open('METADATA')
sed(r'[1-9]\.[0-9]{1,2}\.[0-9]{1,3}-dev',
options.version,
@@ -456,7 +456,7 @@
if not args.use_existing_work_branch:
subprocess.check_call(['git', 'commit', '-a', '-m', git_message])
else:
- print ('Not committing when --use-existing-work-branch. '
+ print('Not committing when --use-existing-work-branch. '
+ 'Commit message should be:\n\n'
+ git_message
+ '\n')
@@ -497,23 +497,23 @@
release_id = gmaven_publisher_stage(
args, [library_gfile, configuration_gfile])
- print "Staged Release ID " + release_id + ".\n"
+ print("Staged Release ID " + release_id + ".\n")
gmaven_publisher_stage_redir_test_info(
release_id,
"com.android.tools:%s:%s" % (DESUGAR_JDK_LIBS, library_version),
library_jar)
- print
+ print("")
input = raw_input("Continue with publishing [y/N]:")
if input != 'y':
- print 'Aborting release to Google maven'
+ print('Aborting release to Google maven')
sys.exit(1)
gmaven_publisher_publish(args, release_id)
- print
- print "Published. Use the email workflow for approval."
+ print("")
+ print("Published. Use the email workflow for approval.")
return make_release
@@ -524,7 +524,7 @@
dirs = os.listdir(
os.path.join('com', 'android', 'tools', DESUGAR_JDK_LIBS_CONFIGURATION))
if len(dirs) != 1:
- print 'Unexpected archive content, %s' + dirs
+ print('Unexpected archive content, %s' + dirs)
sys.exit(1)
version = dirs[0]
@@ -537,14 +537,14 @@
'%s-%s.pom' % (DESUGAR_JDK_LIBS_CONFIGURATION, version))
version_from_pom = extract_version_from_pom(pom_file)
if version != version_from_pom:
- print 'Version mismatch, %s != %s' % (version, version_from_pom)
+ print('Version mismatch, %s != %s' % (version, version_from_pom))
sys.exit(1)
def check_no_google3_client(args, client_name):
if not args.use_existing_work_branch:
clients = subprocess.check_output('g4 myclients', shell=True)
if ':%s:' % client_name in clients:
- print ("Remove the existing '%s' client before continuing " +
+ print("Remove the existing '%s' client before continuing " +
"(force delete: 'g4 citc -d -f %s'), " +
"or use option --use-existing-work-branch.") % (client_name, client_name)
sys.exit(1)
@@ -563,11 +563,11 @@
def gmaven_publisher_stage(args, gfiles):
if args.dry_run:
- print 'Dry-run, would have staged %s' % gfiles
+ print('Dry-run, would have staged %s' % gfiles)
return 'dry-run-release-id'
- print "Staging: %s" % ', '.join(gfiles)
- print
+ print("Staging: %s" % ', '.join(gfiles))
+ print("")
cmd = [GMAVEN_PUBLISHER, 'stage', '--gfile', ','.join(gfiles)]
output = subprocess.check_output(cmd)
@@ -579,13 +579,13 @@
matches = GMAVEN_PUBLISH_STAGE_RELEASE_ID_PATTERN.findall(output)
if matches == None or len(matches) > 1:
- print ("Could not determine the release ID from the gmaven_publisher " +
- "output. Expected a line with 'Release ID = <release id>'.")
- print "Output was:"
- print output
+ print("Could not determine the release ID from the gmaven_publisher " +
+ "output. Expected a line with 'Release ID = <release id>'.")
+ print("Output was:")
+ print(output)
sys.exit(1)
- print output
+ print(output)
release_id = matches[0]
return release_id
@@ -604,7 +604,7 @@
+ "-Dartifact=%s "
+ "-Ddest=%s") % (artifact, dst)
- print """To test the staged content with 'redir' run:
+ print("""To test the staged content with 'redir' run:
%s
@@ -620,12 +620,12 @@
rm -rf /tmp/maven_repo_local
%s
-""" % (redir_command, get_command)
+""" % (redir_command, get_command))
def gmaven_publisher_publish(args, release_id):
if args.dry_run:
- print 'Dry-run, would have published %s' % release_id
+ print('Dry-run, would have published %s' % release_id)
return
cmd = [GMAVEN_PUBLISHER, 'publish', release_id]
@@ -636,11 +636,11 @@
for line in diff.splitlines():
if line.startswith('-R8') and \
line != "-R8_DEV_BRANCH = '%s'" % old_version:
- print line
+ print(line)
invalid_line = line
elif line.startswith('+R8') and \
line != "+R8_DEV_BRANCH = '%s'" % new_version:
- print line
+ print(line)
invalid_line = line
return invalid_line
@@ -648,15 +648,15 @@
def validate_branch_change_diff(version_diff_output, old_version, new_version):
invalid = branch_change_diff(version_diff_output, old_version, new_version)
if invalid:
- print
- print "The diff for the branch change in tools/release.py is not as expected:"
- print
- print "=" * 80
- print version_diff_output
- print "=" * 80
- print
- print "Validate the uploaded CL before landing."
- print
+ print("")
+ print("The diff for the branch change in tools/release.py is not as expected:")
+ print("")
+ print("=" * 80)
+ print(version_diff_output)
+ print("=" * 80)
+ print("")
+ print("Validate the uploaded CL before landing.")
+ print("")
def prepare_branch(args):
@@ -668,7 +668,7 @@
semver = utils.check_basic_semver_version(
branch_version, ", release branch version should be x.y", 2)
if not semver.larger_than(current_semver):
- print ('New branch version "'
+ print('New branch version "'
+ branch_version
+ '" must be strictly larger than the current "'
+ R8_DEV_BRANCH
@@ -703,14 +703,14 @@
if not options.dry_run:
input = raw_input('Create new branch for %s [y/N]:' % branch_version)
if input != 'y':
- print 'Aborting new branch for %s' % branch_version
+ print('Aborting new branch for %s' % branch_version)
sys.exit(1)
maybe_check_call(options, [
'git', 'push', 'origin', 'HEAD:%s' % branch_version])
maybe_tag(options, full_version)
- print ('Updating tools/r8_release.py to make new dev releases on %s'
+ print('Updating tools/r8_release.py to make new dev releases on %s'
% branch_version)
subprocess.check_call(['git', 'new-branch', 'update-release-script'])
@@ -723,7 +723,7 @@
if result:
break
if not result or not result.group(1):
- print 'Failed to find version label in %s' % THIS_FILE_RELATIVE
+ print('Failed to find version label in %s' % THIS_FILE_RELATIVE)
sys.exit(1)
# Update this file with the new dev branch.
@@ -742,9 +742,9 @@
maybe_check_call(options, ['git', 'cl', 'upload', '-f', '-m', message])
- print
- print 'Make sure to send out the branch change CL for review.'
- print
+ print('')
+ print('Make sure to send out the branch change CL for review.')
+ print('')
return make_branch
@@ -826,12 +826,12 @@
and args.version
and not 'dev' in args.version
and args.bug == []):
- print ("When releasing a release version to Android Studio add the "
+ print("When releasing a release version to Android Studio add the "
+ "list of bugs by using '--bug'")
sys.exit(1)
if args.version and not 'dev' in args.version and args.google3:
- print "You should not roll a release version into google 3"
+ print("You should not roll a release version into google 3")
sys.exit(1)
return args
@@ -843,13 +843,13 @@
if args.new_dev_branch:
if args.google3 or args.studio or args.aosp:
- print 'Cannot create a branch and roll at the same time.'
+ print('Cannot create a branch and roll at the same time.')
sys.exit(1)
targets_to_run.append(prepare_branch(args))
if args.dev_release:
if args.google3 or args.studio or args.aosp:
- print 'Cannot create a dev release and roll at the same time.'
+ print('Cannot create a dev release and roll at the same time.')
sys.exit(1)
targets_to_run.append(prepare_release(args))
@@ -873,10 +873,10 @@
if args.update_desugar_library_in_studio:
if not args.studio:
- print ("--studio required")
+ print("--studio required")
sys.exit(1)
if args.bug == []:
- print ("Update studio mirror of com.android.tools:desugar_jdk_libs "
+ print("Update studio mirror of com.android.tools:desugar_jdk_libs "
+ "requires at least one bug by using '--bug'")
sys.exit(1)
targets_to_run.append(update_desugar_library_in_studio(args))
@@ -885,13 +885,13 @@
for target_closure in targets_to_run:
final_results.append(target_closure(args))
- print '\n\n**************************************************************'
- print 'PRINTING SUMMARY'
- print '**************************************************************\n\n'
+ print('\n\n**************************************************************')
+ print('PRINTING SUMMARY')
+ print('**************************************************************\n\n')
for result in final_results:
if result is not None:
- print result
+ print(result)
if __name__ == '__main__':
diff --git a/tools/run_on_app.py b/tools/run_on_app.py
index 983ce6d..94eae01 100755
--- a/tools/run_on_app.py
+++ b/tools/run_on_app.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
# Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
# for details. All rights reserved. Use of this source code is governed by a
# BSD-style license that can be found in the LICENSE file.
@@ -215,8 +215,7 @@
# Please add bug number for disabled permutations and please explicitly
# do Bug: #BUG in the commit message of disabling to ensure re-enabling
DISABLED_PERMUTATIONS = [
- # (app, version, type), e.g., ('gmail', '180826.15', 'deploy'),
- ('youtube', '15.09', 'deploy'), # b/150267318
+ # (app, version, type), e.g., ('gmail', '180826.15', 'deploy')
]
def get_permutations():
@@ -232,7 +231,7 @@
}
# Check to ensure that we add all variants here.
assert len(APPS) == len(data_providers)
- for app, data in data_providers.iteritems():
+ for app, data in data_providers.items():
for version in data.VERSIONS:
for type in data.VERSIONS[version]:
if (app, version, type) not in DISABLED_PERMUTATIONS:
@@ -276,9 +275,9 @@
else:
working = 1024 * 8
exit_code = 0
- range = options.find_min_xmx_range_size
+ range = int(options.find_min_xmx_range_size)
while working - not_working > range:
- next_candidate = working - ((working - not_working)/2)
+ next_candidate = int(working - ((working - not_working)/2))
print('working: %s, non_working: %s, next_candidate: %s' %
(working, not_working, next_candidate))
extra_args = ['-Xmx%sM' % next_candidate]
diff --git a/tools/toolhelper.py b/tools/toolhelper.py
index 45d84e2..1cfd0f0 100644
--- a/tools/toolhelper.py
+++ b/tools/toolhelper.py
@@ -27,6 +27,10 @@
cmd.append(jdk.GetJavaExecutable())
if extra_args:
cmd.extend(extra_args)
+ agent, args = extract_debug_agent_from_args(args)
+ if agent:
+ cmd.append(
+ '-agentlib:jdwp=transport=dt_socket,server=y,suspend=y,address=*:5005')
if debug:
cmd.append('-ea')
if profile:
@@ -103,3 +107,13 @@
else:
args.append(arg)
return lib, args
+
+def extract_debug_agent_from_args(input_args):
+ agent = False
+ args = []
+ for arg in input_args:
+ if arg in ('--debug-agent', '--debug_agent'):
+ agent = True
+ else:
+ args.append(arg)
+ return agent, args
diff --git a/tools/update_prebuilds_in_android.py b/tools/update_prebuilds_in_android.py
index fe8a3c0..3f30964 100755
--- a/tools/update_prebuilds_in_android.py
+++ b/tools/update_prebuilds_in_android.py
@@ -63,8 +63,8 @@
', as' + dest + ' does not exist already')
def copy_jar_targets(root, target_root, jar_targets, maps):
- srcs = map((lambda t: t[0] + '.jar'), jar_targets)
- dests = map((lambda t: t[1] + '.jar'), jar_targets)
+ srcs = list(map((lambda t: t[0] + '.jar'), jar_targets))
+ dests = list(map((lambda t: t[1] + '.jar'), jar_targets))
copy_targets(root, target_root, srcs, dests, maps=maps)
def copy_other_targets(root, target_root):
@@ -88,7 +88,7 @@
def main_download(hash, maps, targets, target_root, version):
jar_targets = JAR_TARGETS_MAP[targets]
- final_targets = map((lambda t: t[0] + '.jar'), jar_targets) + OTHER_TARGETS
+ final_targets = list(map((lambda t: t[0] + '.jar'), jar_targets)) + OTHER_TARGETS
with utils.TempDir() as root:
for target in final_targets:
if hash:
diff --git a/tools/utils.py b/tools/utils.py
index ee49000..a55ea58 100644
--- a/tools/utils.py
+++ b/tools/utils.py
@@ -169,13 +169,12 @@
logger = ProgressLogger(quiet=quiet) if logging else None
failed = False
while True:
- line = process.stdout.readline()
- if line != b'':
+ line = process.stdout.readline().decode('utf-8')
+ if line != '':
stripped = line.rstrip()
stdout.append(stripped)
if logger:
logger.log(stripped)
-
# TODO(christofferqa): r8 should fail with non-zero exit code.
if ('AssertionError:' in stripped
or 'CompilationError:' in stripped
@@ -269,7 +268,7 @@
cmd = ['git', 'rev-parse', 'HEAD']
PrintCmd(cmd)
with ChangedWorkingDirectory(checkout):
- return subprocess.check_output(cmd).strip()
+ return subprocess.check_output(cmd).decode('utf-8').strip()
def makedirs_if_needed(path):
try:
@@ -512,7 +511,7 @@
def check_java_version():
cmd= [jdk.GetJavaExecutable(), '-version']
output = subprocess.check_output(cmd, stderr = subprocess.STDOUT)
- m = re.search('openjdk version "([^"]*)"', output)
+ m = re.search('openjdk version "([^"]*)"', output.decode('utf-8'))
if m is None:
raise Exception("Can't check java version: no version string in output"
" of 'java -version': '{}'".format(output))