Support for adding synthetic classpath classes to lazy app
This is used to retain the classes that D8 synthesizes on the classpath as a result of desugaring. These classes are needed in R8 partial to avoid missing class errors.
Change-Id: Id81b86a03cc0a03a3246c47843b32ee4ea5a5761
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 bed4416..f993bbb 100644
--- a/src/main/java/com/android/tools/r8/graph/DexApplication.java
+++ b/src/main/java/com/android/tools/r8/graph/DexApplication.java
@@ -257,6 +257,8 @@
return self();
}
+ public abstract T addClasspathClass(DexClasspathClass clazz);
+
public List<DexProgramClass> getProgramClasses() {
return programClasses;
}
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 57225a5..8a4946c 100644
--- a/src/main/java/com/android/tools/r8/graph/DexClasspathClass.java
+++ b/src/main/java/com/android/tools/r8/graph/DexClasspathClass.java
@@ -66,7 +66,9 @@
annotations,
origin,
skipNameValidationForTesting);
- assert kind == Kind.CF : "Invalid kind " + kind + " for class-path class " + type;
+ // Allow moving synthetic classes to the classpath (R8 partial only).
+ assert kind == null || kind == Kind.CF
+ : "Invalid kind " + kind + " for class-path class " + type;
}
public static DexClasspathClass toClasspathClass(DexProgramClass programClass) {
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 3e0ccb4..f61ce8e 100644
--- a/src/main/java/com/android/tools/r8/graph/DirectMappedDexApplication.java
+++ b/src/main/java/com/android/tools/r8/graph/DirectMappedDexApplication.java
@@ -260,11 +260,17 @@
return this;
}
+ @Override
public Builder addClasspathClass(DexClasspathClass clazz) {
pendingClasspathClasses.add(clazz);
return self();
}
+ public Builder addClasspathClasses(Collection<DexClasspathClass> classes) {
+ pendingClasspathClasses.addAll(classes);
+ return self();
+ }
+
private void commitPendingClasspathClasses() {
if (!pendingClasspathClasses.isEmpty()) {
classpathClasses =
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 0fc1f96..2652634 100644
--- a/src/main/java/com/android/tools/r8/graph/LazyLoadedDexApplication.java
+++ b/src/main/java/com/android/tools/r8/graph/LazyLoadedDexApplication.java
@@ -19,6 +19,7 @@
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Sets;
import java.util.Collections;
+import java.util.IdentityHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
@@ -31,6 +32,7 @@
private final ProgramClassCollection programClasses;
private final ClasspathClassCollection classpathClasses;
+ private final Map<DexType, DexClasspathClass> synthesizedClasspathClasses;
private final LibraryClassCollection libraryClasses;
private final List<KeepDeclaration> keepDeclarations;
@@ -41,6 +43,7 @@
ProgramClassCollection programClasses,
ImmutableList<DataResourceProvider> dataResourceProviders,
ClasspathClassCollection classpathClasses,
+ Map<DexType, DexClasspathClass> synthesizedClasspathClasses,
LibraryClassCollection libraryClasses,
List<KeepDeclaration> keepDeclarations,
InternalOptions options,
@@ -48,6 +51,7 @@
super(proguardMap, flags, dataResourceProviders, options, timing);
this.programClasses = programClasses;
this.classpathClasses = classpathClasses;
+ this.synthesizedClasspathClasses = synthesizedClasspathClasses;
this.libraryClasses = libraryClasses;
this.keepDeclarations = keepDeclarations;
}
@@ -58,8 +62,7 @@
@Override
List<DexProgramClass> programClasses() {
- programClasses.forceLoad(t -> true);
- return programClasses.getAllClasses();
+ return programClasses.forceLoad().getAllClasses();
}
@Override
@@ -82,8 +85,20 @@
|| !addClassToBuilderIfNotNull(programClasses.get(type), builder::add)) {
// When looking up a type that exists both on program path and classpath, we assume the
// program class is taken and only if not present will look at classpath.
+ DexClasspathClass classpathClass;
if (classpathClasses != null) {
- addClassToBuilderIfNotNull(classpathClasses.get(type), builder::add);
+ classpathClass = classpathClasses.get(type);
+ addClassToBuilderIfNotNull(classpathClass, builder::add);
+ } else {
+ classpathClass = null;
+ }
+
+ if (synthesizedClasspathClasses != null) {
+ if (classpathClass == null) {
+ addClassToBuilderIfNotNull(synthesizedClasspathClasses.get(type), builder::add);
+ } else {
+ assert !synthesizedClasspathClasses.containsKey(type);
+ }
}
}
return builder.build();
@@ -105,6 +120,9 @@
if (clazz == null && classpathClasses != null) {
clazz = classpathClasses.get(type);
}
+ if (clazz == null && synthesizedClasspathClasses != null) {
+ clazz = synthesizedClasspathClasses.get(type);
+ }
if (clazz == null && libraryClasses != null) {
clazz = libraryClasses.get(type);
}
@@ -129,6 +147,7 @@
AllClasses(
LibraryClassCollection libraryClassesLoader,
ClasspathClassCollection classpathClassesLoader,
+ Map<DexType, DexClasspathClass> synthesizedClasspathClasses,
ProgramClassCollection programClassesLoader,
InternalOptions options) {
@@ -152,18 +171,20 @@
// Program classes should be fully loaded.
assert programClassesLoader != null;
assert programClassesLoader.isFullyLoaded();
- programClassesLoader.forceLoad(type -> true);
ImmutableMap<DexType, DexProgramClass> allProgramClasses =
- programClassesLoader.getAllClassesInMap();
+ programClassesLoader.forceLoad().getAllClassesInMap();
// Force-load classpath classes.
- ImmutableMap<DexType, DexClasspathClass> allClasspathClasses;
+ ImmutableMap.Builder<DexType, DexClasspathClass> allClasspathClassesBuilder =
+ ImmutableMap.builder();
if (classpathClassesLoader != null) {
- classpathClassesLoader.forceLoad(type -> true);
- allClasspathClasses = classpathClassesLoader.getAllClassesInMap();
- } else {
- allClasspathClasses = ImmutableMap.of();
+ classpathClassesLoader.forceLoad().forEach(allClasspathClassesBuilder::put);
}
+ if (synthesizedClasspathClasses != null) {
+ allClasspathClassesBuilder.putAll(synthesizedClasspathClasses);
+ }
+ ImmutableMap<DexType, DexClasspathClass> allClasspathClasses =
+ allClasspathClassesBuilder.build();
// Collect loaded classes in the precedence order library classes, program classes and
// class path classes or program classes, classpath classes and library classes depending
@@ -254,24 +275,28 @@
* Force load all classes and return type -> class map containing all the classes.
*/
public AllClasses loadAllClasses() {
- return new AllClasses(libraryClasses, classpathClasses, programClasses, options);
+ return new AllClasses(
+ libraryClasses, classpathClasses, synthesizedClasspathClasses, programClasses, options);
}
public static class Builder extends DexApplication.Builder<Builder> {
private ClasspathClassCollection classpathClasses;
+ private Map<DexType, DexClasspathClass> synthesizedClasspathClasses;
private LibraryClassCollection libraryClasses;
private List<KeepDeclaration> keepDeclarations = Collections.emptyList();
Builder(InternalOptions options, Timing timing) {
super(options, timing);
this.classpathClasses = ClasspathClassCollection.empty();
+ this.synthesizedClasspathClasses = null;
this.libraryClasses = LibraryClassCollection.empty();
}
private Builder(LazyLoadedDexApplication application) {
super(application);
this.classpathClasses = application.classpathClasses;
+ this.synthesizedClasspathClasses = application.synthesizedClasspathClasses;
this.libraryClasses = application.libraryClasses;
}
@@ -303,6 +328,17 @@
}
@Override
+ public Builder addClasspathClass(DexClasspathClass clazz) {
+ if (synthesizedClasspathClasses == null) {
+ synthesizedClasspathClasses = new IdentityHashMap<>();
+ }
+ assert classpathClasses.get(clazz.getType()) == null;
+ assert !synthesizedClasspathClasses.containsKey(clazz.getType());
+ synthesizedClasspathClasses.put(clazz.getType(), clazz);
+ return this;
+ }
+
+ @Override
public LazyLoadedDexApplication build() {
ProgramClassConflictResolver resolver =
options.programClassConflictResolver == null
@@ -314,6 +350,7 @@
ProgramClassCollection.create(getProgramClasses(), resolver),
ImmutableList.copyOf(dataResourceProviders),
classpathClasses,
+ synthesizedClasspathClasses,
libraryClasses,
keepDeclarations,
options,
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 146633f..17e5a83 100644
--- a/src/main/java/com/android/tools/r8/synthesis/SyntheticItems.java
+++ b/src/main/java/com/android/tools/r8/synthesis/SyntheticItems.java
@@ -1203,9 +1203,9 @@
} else {
appBuilder.addProgramClass(definition.asProgramDefinition().getHolder());
}
- } else if (appBuilder.isDirect()) {
+ } else {
assert definition.isClasspathDefinition();
- appBuilder.asDirect().addClasspathClass(definition.asClasspathDefinition().getHolder());
+ appBuilder.addClasspathClass(definition.asClasspathDefinition().getHolder());
}
builder.addItem(definition);
}
diff --git a/src/main/java/com/android/tools/r8/utils/ClassMap.java b/src/main/java/com/android/tools/r8/utils/ClassMap.java
index ea5e988..6871538 100644
--- a/src/main/java/com/android/tools/r8/utils/ClassMap.java
+++ b/src/main/java/com/android/tools/r8/utils/ClassMap.java
@@ -3,6 +3,8 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.utils;
+import static com.google.common.base.Predicates.alwaysTrue;
+
import com.android.tools.r8.errors.CompilationError;
import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.graph.ClassKind;
@@ -19,6 +21,7 @@
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicReference;
+import java.util.function.BiConsumer;
import java.util.function.Predicate;
import java.util.function.Supplier;
@@ -148,15 +151,17 @@
}
public ImmutableMap<DexType, T> getAllClassesInMap() {
+ ImmutableMap.Builder<DexType, T> builder = ImmutableMap.builder();
+ // This is fully loaded, so the class map will no longer change.
+ forEach(builder::put);
+ return builder.build();
+ }
+
+ public void forEach(BiConsumer<DexType, T> consumer) {
if (classProvider.get() != null) {
throw new Unreachable("Getting all classes from not fully loaded collection.");
}
- ImmutableMap.Builder<DexType, T> builder = ImmutableMap.builder();
- // This is fully loaded, so the class map will no longer change.
- for (Map.Entry<DexType, Supplier<T>> entry : classes.entrySet()) {
- builder.put(entry.getKey(), entry.getValue().get());
- }
- return builder.build();
+ classes.forEach((type, supplier) -> consumer.accept(type, supplier.get()));
}
public Iterable<DexType> getAllTypes() {
@@ -172,6 +177,10 @@
"Cannot access all types since the classProvider is no longer available");
}
+ public ClassMap<T> forceLoad() {
+ return forceLoad(alwaysTrue());
+ }
+
/**
* Forces loading of all the classes satisfying the criteria specified.
*
@@ -180,13 +189,13 @@
* these classes will never be loaded.
*/
@SuppressWarnings("ReferenceEquality")
- public void forceLoad(Predicate<DexType> load) {
+ public ClassMap<T> forceLoad(Predicate<DexType> load) {
Set<DexType> knownClasses;
ClassProvider<T> classProvider;
// Cache value of class provider, as it might change concurrently.
if (isFullyLoaded()) {
- return;
+ return this;
}
classProvider = this.classProvider.get();
@@ -213,7 +222,7 @@
// only one thread proceeds to rewriting the map.
synchronized (this) {
if (this.classProvider.get() == null) {
- return; // Has been force-loaded concurrently.
+ return this; // Has been force-loaded concurrently.
}
// We avoid calling get() on a class supplier unless we know it was loaded.
@@ -244,6 +253,8 @@
// classes by blocking on 'this' and hence wait for the loading operation to finish.
this.classProvider.set(null);
}
+
+ return this;
}
public boolean isFullyLoaded() {