R8 nest warning and errors
- Tree pruner works with incomplete nest
- Either NestDesugaring or NestReducer raise
nest warnings and errors.
- Basic version of Nest Reducer added, it is
an alternative to desugaring for the Cf back-end
and it tries to remove nests if they do not use
nest access control.
Bug:132682295
Change-Id: Ib9ac6e1abe4ce513a00ce67fe0b69967bef9ed93
diff --git a/src/main/java/com/android/tools/r8/R8.java b/src/main/java/com/android/tools/r8/R8.java
index 0e69108..e92c6f5 100644
--- a/src/main/java/com/android/tools/r8/R8.java
+++ b/src/main/java/com/android/tools/r8/R8.java
@@ -29,6 +29,7 @@
import com.android.tools.r8.ir.desugar.R8NestBasedAccessDesugaring;
import com.android.tools.r8.ir.optimize.EnumOrdinalMapCollector;
import com.android.tools.r8.ir.optimize.MethodPoolCollection;
+import com.android.tools.r8.ir.optimize.NestReducer;
import com.android.tools.r8.ir.optimize.SwitchMapCollector;
import com.android.tools.r8.ir.optimize.UninstantiatedTypeOptimization;
import com.android.tools.r8.ir.optimize.UnusedArgumentsCollector;
@@ -425,7 +426,7 @@
AppView<AppInfoWithLiveness> appViewWithLiveness = appView.withLiveness();
appView.setGraphLense(new MemberRebindingAnalysis(appViewWithLiveness).run());
- if (options.enableNestBasedAccessDesugaring) {
+ if (options.enableNestBasedAccessDesugaring && !options.canUseNestBasedAccess()) {
timing.begin("NestBasedAccessDesugaring");
R8NestBasedAccessDesugaring analyzer = new R8NestBasedAccessDesugaring(appViewWithLiveness);
boolean changed =
@@ -437,6 +438,14 @@
.rewrittenWithLense(application.asDirect(), appView.graphLense()));
}
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();
}
if (options.enableHorizontalClassMerging) {
timing.begin("HorizontalStaticClassMerger");
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
index fc21c3d..0f3944d 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/D8NestBasedAccessDesugaring.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/D8NestBasedAccessDesugaring.java
@@ -152,4 +152,14 @@
protected boolean shouldProcessClassInNest(DexClass clazz, List<DexType> nest) {
return clazz.isNotProgramClass();
}
+
+ @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/NestBasedAccessDesugaring.java b/src/main/java/com/android/tools/r8/ir/desugar/NestBasedAccessDesugaring.java
index 8564183..ddef220 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/NestBasedAccessDesugaring.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/NestBasedAccessDesugaring.java
@@ -5,9 +5,6 @@
package com.android.tools.r8.ir.desugar;
import com.android.tools.r8.dex.Constants;
-import com.android.tools.r8.errors.CompilationError;
-import com.android.tools.r8.errors.IncompleteNestNestDesugarDiagnosic;
-import com.android.tools.r8.errors.MissingNestHostNestDesugarDiagnostic;
import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.ClassAccessFlags;
@@ -26,9 +23,7 @@
import com.android.tools.r8.graph.NestMemberClassAttribute;
import com.android.tools.r8.graph.UseRegistry;
import com.android.tools.r8.ir.code.Invoke;
-import com.android.tools.r8.origin.Origin;
import com.android.tools.r8.origin.SynthesizedOrigin;
-import com.android.tools.r8.position.Position;
import com.android.tools.r8.utils.BooleanUtils;
import java.util.ArrayList;
import java.util.Collections;
@@ -76,7 +71,11 @@
return nestConstructor.type;
}
- protected DexClass definitionFor(DexType type) {
+ abstract void reportMissingNestHost(DexClass clazz);
+
+ abstract void reportIncompleteNest(List<DexType> nest);
+
+ DexClass definitionFor(DexType type) {
return appView.definitionFor(appView.graphLense().lookupType(type));
}
@@ -145,79 +144,6 @@
protected abstract boolean shouldProcessClassInNest(DexClass clazz, List<DexType> nest);
- private void reportIncompleteNest(List<DexType> nest) {
- List<String> programClassesFromNest = new ArrayList<>();
- List<String> unavailableClasses = new ArrayList<>();
- List<String> classPathClasses = new ArrayList<>();
- List<String> libraryClasses = new ArrayList<>();
- DexClass availableProgramClass = null;
- for (DexType type : nest) {
- DexClass clazz = definitionFor(type);
- if (clazz == null) {
- unavailableClasses.add(type.getName());
- } else if (clazz.isLibraryClass()) {
- libraryClasses.add(type.getName());
- } else if (clazz.isProgramClass()) {
- programClassesFromNest.add(type.getName());
- availableProgramClass = clazz;
- } else {
- assert clazz.isClasspathClass();
- classPathClasses.add(type.getName());
- }
- }
- StringBuilder stringBuilder =
- new StringBuilder("Compilation of classes ")
- .append(String.join(", ", programClassesFromNest))
- .append(" requires its nest mates ");
- if (!unavailableClasses.isEmpty()) {
- stringBuilder.append(String.join(", ", unavailableClasses)).append(" (unavailable) ");
- }
- if (!libraryClasses.isEmpty()) {
- stringBuilder.append(String.join(", ", unavailableClasses)).append(" (on library path) ");
- }
- stringBuilder.append("to be on program or class path for compilation to succeed)");
- if (!classPathClasses.isEmpty()) {
- stringBuilder
- .append("(Classes ")
- .append(String.join(", ", classPathClasses))
- .append(" from the same nest are on class path).");
- }
- if (!libraryClasses.isEmpty()) {
- throw new CompilationError(stringBuilder.toString());
- }
- Origin origin;
- if (availableProgramClass == null) {
- origin = Origin.unknown();
- } else {
- origin = availableProgramClass.getOrigin();
- }
- appView
- .options()
- .reporter
- .warning(
- new IncompleteNestNestDesugarDiagnosic(
- origin, Position.UNKNOWN, stringBuilder.toString()));
- }
-
- private void reportMissingNestHost(DexClass compiledClass) {
- String nestHostName = compiledClass.getNestHost().getName();
- String message =
- "Class "
- + compiledClass.type.getName()
- + " requires its nest host "
- + nestHostName
- + " to be on program or class path for compilation to succeed.";
- if (compiledClass.isLibraryClass()) {
- throw new CompilationError(message);
- }
- appView
- .options()
- .reporter
- .warning(
- new MissingNestHostNestDesugarDiagnostic(
- compiledClass.getOrigin(), Position.UNKNOWN, message));
- }
-
private DexProgramClass createNestAccessConstructor() {
return new DexProgramClass(
appView.dexItemFactory().createType(FULL_NEST_CONTRUCTOR_NAME),
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
index 4210ac2..d2a06b9 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/R8NestBasedAccessDesugaring.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/R8NestBasedAccessDesugaring.java
@@ -14,8 +14,8 @@
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.GraphLense;
import com.android.tools.r8.utils.ThreadUtils;
+import com.google.common.collect.Sets;
import java.util.ArrayList;
-import java.util.Collections;
import java.util.IdentityHashMap;
import java.util.List;
import java.util.Map;
@@ -41,9 +41,7 @@
public GraphLense run(ExecutorService executorService, DexApplication.Builder<?> appBuilder)
throws ExecutionException {
- if (appView.options().canUseNestBasedAccess()) {
- return appView.graphLense();
- }
+ assert !appView.options().canUseNestBasedAccess();
computeAndProcessNestsConcurrently(executorService);
addDeferredBridgesAndMapMethods();
clearNestAttributes();
@@ -97,7 +95,8 @@
private void computeAndProcessNestsConcurrently(ExecutorService executorService)
throws ExecutionException {
- Set<DexType> nestHosts = Collections.newSetFromMap(new IdentityHashMap<>());
+ 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).
@@ -121,4 +120,21 @@
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/optimize/NestReducer.java b/src/main/java/com/android/tools/r8/ir/optimize/NestReducer.java
new file mode 100644
index 0000000..97c9eb7
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/optimize/NestReducer.java
@@ -0,0 +1,137 @@
+// 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;
+
+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.DexProgramClass;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.NestMemberClassAttribute;
+import com.android.tools.r8.utils.ThreadUtils;
+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.
+public class NestReducer {
+
+ private AppView<?> appView;
+
+ public NestReducer(AppView<?> appView) {
+ this.appView = appView;
+ }
+
+ private DexClass definitionFor(DexType type) {
+ assert appView.graphLense().lookupType(type) == type;
+ return appView.definitionFor(appView.graphLense().lookupType(type));
+ }
+
+ 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.
+ for (DexProgramClass clazz : appView.appInfo().classes()) {
+ if (clazz.isInANest()) {
+ DexType hostType = clazz.getNestHost();
+ if (!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);
+ }
+ 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.graphLense().lookupType(attr.getNestMember()));
+ member.clearNestHost();
+ }
+ }
+
+ private boolean hasPrivateMembers(DexClass clazz) {
+ for (DexEncodedMethod method : clazz.methods()) {
+ if (method.accessFlags.isPrivate()) {
+ return true;
+ }
+ }
+ for (DexEncodedField field : clazz.fields()) {
+ if (field.accessFlags.isPrivate()) {
+ return true;
+ }
+ }
+ return false;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/shaking/TreePruner.java b/src/main/java/com/android/tools/r8/shaking/TreePruner.java
index 52b0ce4..97b440d 100644
--- a/src/main/java/com/android/tools/r8/shaking/TreePruner.java
+++ b/src/main/java/com/android/tools/r8/shaking/TreePruner.java
@@ -144,21 +144,33 @@
}
private void clearDeadNestMembers(DexClass nestHost) {
+ // null definition should raise a warning which is raised later on in Nest specific passes.
nestHost
.getNestMembersClassAttributes()
- .removeIf(nestMemberAttr -> !isTypeLive(nestMemberAttr.getNestMember()));
+ .removeIf(
+ nestMemberAttr ->
+ appView.definitionFor(nestMemberAttr.getNestMember()) != null
+ && !isTypeLive(nestMemberAttr.getNestMember()));
}
private void claimNestOwnership(DexClass newHost) {
DexClass previousHost = appView.definitionFor(newHost.getNestHost());
- assert previousHost != null;
+ if (previousHost == null) {
+ // Nest host will be cleared from all nest members in Nest specific passes.
+ return;
+ }
newHost.clearNestHost();
for (NestMemberClassAttribute attr : previousHost.getNestMembersClassAttributes()) {
if (attr.getNestMember() != newHost.type && isTypeLive(attr.getNestMember())) {
DexClass nestMember = appView.definitionFor(attr.getNestMember());
- assert nestMember != null;
- nestMember.setNestHost(newHost.type);
- newHost.getNestMembersClassAttributes().add(new NestMemberClassAttribute(nestMember.type));
+ if (nestMember != null) {
+ nestMember.setNestHost(newHost.type);
+ }
+ // We still need to add it, even if the definition is null,
+ // so the warning / error are correctly raised in Nest specific passes.
+ newHost
+ .getNestMembersClassAttributes()
+ .add(new NestMemberClassAttribute(attr.getNestMember()));
}
}
}
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 bd17e99..7fa1866 100644
--- a/src/main/java/com/android/tools/r8/utils/InternalOptions.java
+++ b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
@@ -13,9 +13,12 @@
import com.android.tools.r8.Version;
import com.android.tools.r8.dex.Marker;
import com.android.tools.r8.errors.CompilationError;
+import com.android.tools.r8.errors.IncompleteNestNestDesugarDiagnosic;
import com.android.tools.r8.errors.InterfaceDesugarMissingTypeDiagnostic;
import com.android.tools.r8.errors.InvalidDebugInfoException;
+import com.android.tools.r8.errors.MissingNestHostNestDesugarDiagnostic;
import com.android.tools.r8.experimental.graphinfo.GraphConsumer;
+import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexClass;
import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.graph.DexItem;
@@ -291,6 +294,8 @@
public boolean enableDesugaring = true;
// Flag to turn on/off JDK11+ nest-access control
public boolean enableNestBasedAccessDesugaring = false;
+ // Flag to turn on/off reduction of nest to improve class merging optimizations.
+ public boolean enableNestReduction = true;
// Defines interface method rewriter behavior.
public OffOrAuto interfaceMethodDesugaring = OffOrAuto.Auto;
// Defines try-with-resources rewriter behavior.
@@ -473,6 +478,127 @@
/** A set of dexitems we have reported missing to dedupe warnings. */
private final Set<DexItem> reportedMissingForDesugaring = Sets.newConcurrentHashSet();
+ public enum NestReportType {
+ FATAL_ERROR,
+ DESUGAR_WARNING,
+ WARNING
+ }
+
+ 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(
+ new MissingNestHostNestDesugarDiagnostic(
+ compiledClass.getOrigin(),
+ Position.UNKNOWN,
+ messageWarningMissingNestHost(compiledClass)));
+ }
+
+ 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) {
+ String nestHostName = compiledClass.getNestHost().getName();
+ return "Class "
+ + compiledClass.type.getName()
+ + " requires its nest host "
+ + nestHostName
+ + " 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.";
+ }
+
+ 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.graphLense().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());
+ }
+ }
+ StringBuilder stringBuilder =
+ new StringBuilder("Compilation of classes ")
+ .append(String.join(", ", programClassesFromNest))
+ .append(" requires its nest mates ");
+ if (!unavailableClasses.isEmpty()) {
+ stringBuilder.append(String.join(", ", unavailableClasses)).append(" (unavailable) ");
+ }
+ if (!libraryClasses.isEmpty()) {
+ stringBuilder.append(String.join(", ", unavailableClasses)).append(" (on library path) ");
+ }
+ stringBuilder.append("to be on program or class path.");
+ if (!classPathClasses.isEmpty()) {
+ stringBuilder
+ .append("(Classes ")
+ .append(String.join(", ", classPathClasses))
+ .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, DexType contextType) {
if (reportedMissingForDesugaring.add(missingType)) {
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 b2adfce..f41f27d 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
@@ -104,7 +104,11 @@
.addKeepAllAttributes()
.setMinApi(parameters.getApiLevel())
.addProgramFiles(classesOfNest(nestID))
- .addOptionsModification(options -> options.enableNestBasedAccessDesugaring = true)
+ .addOptionsModification(
+ options -> {
+ options.enableNestBasedAccessDesugaring = true;
+ options.enableNestReduction = false;
+ })
.compile()
.run(parameters.getRuntime(), getMainClass(nestID))
.assertSuccessWithOutput(getExpectedResult(nestID));
@@ -146,6 +150,7 @@
.addOptionsModification(
options -> {
options.enableNestBasedAccessDesugaring = true;
+ options.enableNestReduction = false;
})
.addProgramFiles(JAR)
.setMinApi(minApi)
diff --git a/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/NestClassMergingTest.java b/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/NestClassMergingTest.java
index abc22b7..934de31 100644
--- a/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/NestClassMergingTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/NestClassMergingTest.java
@@ -94,6 +94,7 @@
// unused.
options.enableValuePropagation = false;
options.enableClassInlining = false;
+ options.enableNestReduction = false;
})
.enableInliningAnnotations("nestHostExample")
.setMinApi(parameters.getApiLevel())
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 601e634..48fe91b 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
@@ -19,6 +19,8 @@
import com.android.tools.r8.TestParametersCollection;
import com.android.tools.r8.TestRuntime.CfVm;
import com.android.tools.r8.ToolHelper.DexVm;
+import com.android.tools.r8.errors.IncompleteNestNestDesugarDiagnosic;
+import com.android.tools.r8.errors.MissingNestHostNestDesugarDiagnostic;
import java.nio.file.Path;
import java.util.List;
import org.hamcrest.Matcher;
@@ -49,28 +51,19 @@
@Test
public void testWarningD8() throws Exception {
- // TODO (b/132676197): use desugaring handling
Assume.assumeTrue(parameters.isDexRuntime());
- testIncompleteNestWarning(true);
- testMissingNestHostWarning(true);
+ testIncompleteNestWarning(true, true);
+ testMissingNestHostWarning(true, true);
}
@Test
public void testWarningR8() throws Exception {
- // TODO (b/132676197): use desugaring handling
- // TODO (b/132676197): Cf backend should raise a warning
- // Remove Assume when fixed.
- Assume.assumeTrue(parameters.isDexRuntime());
- testIncompleteNestWarning(false);
- testMissingNestHostWarning(false);
+ testIncompleteNestWarning(false, parameters.isDexRuntime());
+ testMissingNestHostWarning(false, parameters.isDexRuntime());
}
@Test
public void testErrorR8() {
- // TODO (b/132676197): Cf back should raise an error
- // TODO (b/132676197): Dex back-end should raise an error
- // Remove Assume when fixed.
- Assume.assumeTrue(false);
testMissingNestHostError();
testIncompleteNestError();
}
@@ -111,7 +104,7 @@
compileOnlyClassesMatching(innerClassMatcher, false, false);
fail("Should have raised an exception for missing nest host");
} catch (Exception e) {
- assertTrue(e.getCause().getMessage().contains("requires its nest host"));
+ assertTrue(e.getCause().getCause().getMessage().contains("requires its nest host"));
}
}
@@ -121,28 +114,40 @@
compileOnlyClassesMatching(innerClassMatcher, false, false);
fail("Should have raised an exception for incomplete nest");
} catch (Exception e) {
- assertTrue(e.getCause().getMessage().contains("requires its nest mates"));
+ assertTrue(e.getCause().getCause().getMessage().contains("requires its nest mates"));
}
}
- private void testMissingNestHostWarning(boolean d8) throws Exception {
+ private void testMissingNestHostWarning(boolean d8, boolean desugarWarning) throws Exception {
Matcher<String> innerClassMatcher =
containsString("BasicNestHostWithInnerClassMethods$BasicNestedClass");
TestCompileResult compileResult = compileOnlyClassesMatching(innerClassMatcher, d8, true);
assertTrue(compileResult.getDiagnosticMessages().getWarnings().size() >= 1);
- assertTrue(
- compileResult.getDiagnosticMessages().getWarnings().stream()
- .anyMatch(
- warning -> warning.getDiagnosticMessage().contains("requires its nest host")));
+ if (desugarWarning) {
+ assertTrue(
+ compileResult.getDiagnosticMessages().getWarnings().stream()
+ .anyMatch(warn -> warn instanceof MissingNestHostNestDesugarDiagnostic));
+ } else 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")));
+ }
}
- private void testIncompleteNestWarning(boolean d8) throws Exception {
+ private void testIncompleteNestWarning(boolean d8, boolean desugarWarning) throws Exception {
Matcher<String> innerClassMatcher = endsWith("BasicNestHostWithInnerClassMethods");
TestCompileResult compileResult = compileOnlyClassesMatching(innerClassMatcher, d8, true);
assertTrue(compileResult.getDiagnosticMessages().getWarnings().size() >= 1);
- assertTrue(
- compileResult.getDiagnosticMessages().getWarnings().stream()
- .anyMatch(
- warning -> warning.getDiagnosticMessage().contains("requires its nest mates")));
+ if (desugarWarning) {
+ assertTrue(
+ compileResult.getDiagnosticMessages().getWarnings().stream()
+ .anyMatch(warn -> warn instanceof IncompleteNestNestDesugarDiagnosic));
+ } else if (!d8) {
+ // R8 should raise extra warning when cleaning the nest.
+ assertTrue(
+ compileResult.getDiagnosticMessages().getWarnings().stream()
+ .anyMatch(warn -> warn.getDiagnosticMessage().contains("requires its nest mates")));
+ }
}
}